RAP – Step-by-Step Guide to Creating an Overview Page Dashboard with Fiori Elements

Estimated read time 43 min read

Overview page is generally used for grouping Embedded Analytics upon different dimension which flows towards individual entities. But, Have you ever wondered on creating overview page with RAP on Backend. This blog will explain on the same in detail. 

Overview for creating Overview page – Dashboard:

We are going with complete end-end cloud ready approach and for the same I’ve used BTP Cloud foundry Environment for this Dashboard creation. Below are the sequential steps to create overview page Dashboard. 

Backend:

1. Set up BTP Cloud foundry Environment and enable it in Eclipse ADT.

2. Create Individual Entities using RAP Custom Entity/CDS View. 

3. Create Filter Entity which will be enabled as common live Filter

4. Create Business logic class for these Custom Entities.

Front End:

1. Create New Project with Overview Page Template

2. Link the Entities in BAS and create different cards as per the requirement.

3. Dry run the app to debug and check backend and front end is interlinked and fiori element is configured properly. 

Below Flow chart explains the complete process precisely.

Now let’s start the Development. 

 Set up BTP Cloud foundry Environment and enable it in Eclipse ADT

1. Login to your BTP Cockpit as below. 

2. Click Boosters from the left-hand navigation pane of your Global Account view and select prepare an Account for ABAP Trail.

3. The system automatically assigns roles, establishes the Cloud Foundry runtime, builds a dev space, and spins up the ABAP service instance.Once complete, skip down to the Generate a Service Key. Download the service key once the process is completed and store it locally.

4. Now navigate to SubAccount, Instances and Subscriptions and locate the ABAP instance. Right click on 3 dots to create service key.

5. Once service key is created, click on it and navigate to the JSON that it shows. Now, copy the JSON and navigate to Eclipse.

6. Now in Eclipse create new project and enter the JSON that has been copied from BTP Cockpit. This JSON will create the cloud project in Eclipse. 

Create Individual Entities using RAP Custom Entity/CDS View

1. Since BTP Cockpit in Trail does not have valid SAP Predefined Tables, Let’s create the custom ZTable – ZOVP_ANALYTICS as below.

 

@EndUserText.label : ‘Table for analytics’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zovp_analytics {

key client_id : abap.clnt not null;
key orderid : abap.char(10) not null;
region : abap.char(20) not null;
division : abap.char(5);
districhl : abap.char(5);
customer : abap.char(10);
status : abap.char(20);
order_del : abap.int4;
tax : abap.dec(10,3);
@Semantics.amount.currencyCode : ‘zovp_analytics.currency’
totalamount : abap.curr(15,2);
@Semantics.amount.currencyCode : ‘zovp_analytics.currency’
targetsales : abap.curr(15,2);
currency : abap.cuky;

}

Note: Before getting into next steps, let’s have a quick overview on RAP Custom Entity. RAP Custom Entity is built on RAP Query Provider Interface which will collectively carry the logic for the desired Entity Type. This can be directly given as entity in service definition and also can be exposed in service binding.

2. Now for Overview Page Filter, lets create  Separate  RAP Custom Entity with Region as Filter. Note the case sensitive in Filter Entity and Child Entities should be same and with that only $filter will correctly map the appropriate filter while triggering target entities after entering filter value. 

@EndUserText.label: ‘Global Filter Entity’
@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
define root custom entity ZCE_FilterEntity
{
.selectionField: [{ position: 10 }]
@Consumption.valueHelpDefinition: [{ entity: { name: ‘ZCE_VH_Region’, element: ‘Region’ } }]
@EndUserText.label: ‘Region’
key Region : tcode;
}

3. Let’s Create backend RAP Custom Entity – ZCE_RecentOrders for the first Table card for our overview page. In this card the grouping Analytics happens upon Customer, and it will work on Region Filter as well. 

Note:

Before going further let’s understand how this works. As mentioned earlier, overview page is for Embedded Analytics calculation on different metrics. so, if Region is passed in filter, then from the custom entity class first the overall table will be filtered with Region and then the grouping metrics along with aggregation data lone will be taken in another select query and grouped for output. 
We are giving Annotation in each custom entity as the same direction path will be configured in Front end Manifest which will reflect in the overview card. 

@EndUserText.label: ‘Recent Orders for Table Card’
@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
define root custom entity ZCE_RecentOrders
{
.lineItem: [{ position: 10, label: ‘Customer’ }]
key Customer : abap.char(40);

.lineItem: [{ position: 20, label: ‘Status’ }]
Status : abap.char(10);

.hidden : true
StatusColor : abap.int1;

.lineItem: [{ position: 30, label: ‘Amount’ }]
@Semantics.amount.currencyCode: ‘Currency’
OrderAmount : abap.curr(15,2);
.hidden : true
Currency : abap.cuky;
.hidden : true
Region : abap.char(20);
}

4. The custom entity – ZCE_SALES_BAR_LIST_CARD is created for second card and this will be configured as Bar list card template in Overview page. This card has calculative metrics on source and target and for that datapoint annotations were also done. 

@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
@EndUserText.label: ‘Sales Bar Chart List’
define root custom entity ZCE_SALES_BAR_LISTCARD
{
.dataPoint: { title: ‘Order Count’, description: ‘Total Orders’, qualifier: ‘CountKPI’ }
key TotalOrders : abap.int4;
.lineItem: [{ position: 10, label: ‘Status’ }]
key status: abap.char(20);
.lineItem: [{ position: 20, type: #AS_DATAPOINT }]
@Semantics.amount.currencyCode: ‘Currency’
.dataPoint: {
visualization: #PROGRESS,
targetValue: 1000,
criticality: ‘StatusColor’
}
TotalSales : abap.curr(15,2);
.lineItem: [{ position: 30, type: #AS_DATAPOINT, label: ‘Target Value’ }]
.dataPoint: {
title: ‘Target’
}
@Semantics.amount.currencyCode: ‘Currency’
TargetSales : abap.curr(15,2);
StatusColor : abap.int1;
Currency : abap.cuky;
.hidden : true
Region : abap.char(20);
}

5. The custom entity – ZCE_FINAL_DIV_TAX is created for third card and this will be configured as list card template in Overview page. In this card the grouping happens on Division and in addition it will have KPI Header which will give overall Tax Average for the respective filter. Datapoint Annotation is configured for KPI header. 

@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
@EndUserText.label: ‘Tax Average’
define root custom entity ZCE_FINAL_DIV_TAX
{
.lineItem: [{ position: 10, label: ‘Division’ }]
key Division : abap.char(5);
.lineItem: [{ position: 20, label: ‘Tax %’ }]
TaxAverage : abap.dec(15,2);
.dataPoint: { qualifier: ‘SalesData’,
title: ‘Tax Average’,
criticality: ‘StatusColor’,
valueFormat.numberOfFractionalDigits: 2 }
GlobalTaxAverage : abap.dec(15,2);
StatusColor : int1;
.hidden : true
Region : abap.char(20);
}

 6. Now lets create CDS View Entity – ZCDS_REGION_ORDERS instead of RAP Custom Entity for Region card which will be configured as 4th overview card with Table card template. 

Why CDS View for this card ? – Because this card has property Region which will work directly once the Overview filter Region is entered. URL will intrepret $filter Region to this CDS view and since this view has Region Property built within itself and henceforth it can be filtered easily. 

Note: Instead of custom Entity, we can also use CDS with parameters in backend but that will support only if the CDS View is Analytical CDS. But unfortunately RAP V2 Does not support Analytical CDS View. CDS with Analytical is supported in RAP with OData V4, but again unfortunately OData V4 is not supported in BTP Overview page template as of today. So, these are the challenges I faced and to avoid more complications I went with custom entity for the other cards. 

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Orders Delivered by Region’
@Metadata.ignorePropagatedAnnotations: false
define root view entity ZCDS_REGION_ORDERS
as select from zovp_analytics
{
@UI.lineItem: [{ position: 10, label: ‘Region’ }]
key region as Region,
@UI.lineItem: [{ position: 20, label: ‘Orders Delivered’ }]
sum(order_del) as OrdersDelivered,
@UI.lineItem: [{ position: 30, label: ‘Order Amount’ }]
@Semantics.amount.currencyCode: ‘Currency’
sum(totalamount) as OrderAmount,
currency
} group by region, currency;

7. Till now we created Custom Entities for Table and list card. For KPI card, we can create Custom Entity now. In this card Total sales happened for all region would be collectively calculated.

Note: Since this is KPI card, from front end we may choose Analytical card template to show KPI data alone and from backend we must have selection variant and data point annotations. 

@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
@EndUserText.label: ‘Region KPI’
@Metadata.allowExtensions: true
@UI.presentationVariant: [{ qualifier: ‘KPIRegion’,
text: ‘KPI: Total Region Sales’,
visualizations: [{type: #AS_DATAPOINT,
element: ‘TotalAmount’,
qualifier: ‘TotalAmount’ }] }]
@UI.selectionVariant: [{ qualifier: ‘KPIRegion’,
text: ‘KPI: Total Region Sales’ }]
@UI.selectionPresentationVariant: [{ qualifier: ‘KPIRegion’,
presentationVariantQualifier: ‘KPIRegion’,
selectionVariantQualifier: ‘KPIRegion’ }]
define root custom entity ZCE_REGION_KPI
{
@Semantics.amount.currencyCode : ‘Currency’
@UI.kpi: [{ detail: {
defaultPresentationVariantQualifier: ‘KPIRegion’
} }]
@ui.dataPoint: {
description: ‘Total | USD’,
qualifier: ‘KPIRegion’ }
key TotalAmount : abap.curr(15,2);
Currency : abap.cuky;
Region : abap.char(20);
}

8. So, we have custom entity with 4 cards now. To populate data to custom table ZOVP_ANALYTICS, we can create one separate CDS View entity – ZCDS_OVP_TABLE_MAINTAIN from which list report can be created.  

Note: This CDS View Entity alone will be configured as list report from BAS for data maintanence purpose and it can be used for INTENT BASED NAVIGATION as well in future. 

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘TABLE MAINTAIN’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZCDS_OVP_TABLE_MAINTAIN
as select from zovp_analytics
{
.facet: [
{ id: ‘GeneralInfo’,
type: #COLLECTION,
label: ‘General Information’,
position: 10 },
{ id: ‘OrderDetails’,
parentId: ‘GeneralInfo’,
type: #IDENTIFICATION_REFERENCE,
label: ‘Order Details’,
position: 10 }
]
.lineItem: [{ position: 10 }]
.identification: [{ position: 10, label: ‘Order ID’ }]
key orderid as Orderid,
.lineItem: [{ position: 20 }]
.identification: [{ position: 20, label: ‘Region’ }]
.selectionField: [{position: 10 }]
@Consumption.valueHelpDefinition: [{ entity: { name: ‘ZCE_VH_Region’, element: ‘Region’ } }]
region as Region,
.lineItem: [{ position: 30 }]
.identification: [{ position: 30, label: ‘Division’ }]
division as Division,
.lineItem: [{ position: 40 }]
.identification: [{ position: 40, label: ‘Distribution channel’ }]
districhl as Districhl,
.lineItem: [{ position: 50 }]
.identification: [{ position: 50, label: ‘Customer’ }]
customer as Customer,
.lineItem: [{ position: 60 }]
.identification: [{ position: 60, label: ‘Status’ }]
status as Status,
.lineItem: [{ position: 70 }]
.identification: [{ position: 70, label: ‘Orders Delivered’ }]
order_del as OrderDel,
.lineItem: [{ position: 80 }]
.identification: [{ position: 80, label: ‘Tax’ }]
tax as Tax,
.lineItem: [{ position: 90 }]
.identification: [{ position: 90, label: ‘Total Amount’ }]
@Semantics.amount.currencyCode : ‘currency’
totalamount as Totalamount,
.lineItem: [{ position: 100 }]
.identification: [{ position: 100, label: ‘Target Sales’ }]
@Semantics.amount.currencyCode : ‘currency’
targetsales as Targetsales,
.lineItem: [{ position: 110 }]
.identification: [{ position: 110, label: ‘Currency’ }]
currency as Currency
}

9. Create Managed Behaviour Definition to enable CRUD Operations for ZCDS_OVP_TABLE_MAINTAIN. (This Behavior Definition is not linked to Overview page. It is solely created for List report which is just to populate data to main table. If the intent is to create overview page only then probably this can be avoided ). 

managed implementation in class zbp_cds_ovp_table_maintain unique;
strict ( 2 );

define behavior for ZCDS_OVP_TABLE_MAINTAIN alias ovp_table_maintain
persistent table zovp_analytics
lock master
authorization master ( instance )
{
create ( authorization : global );
update;
delete;

mapping for zovp_analytics
{
Orderid = orderid;
Region = region;
Division = division;
Districhl = districhl;
Customer = customer;
Status = status;
OrderDel = order_del;
Tax = tax;
TotalAmount = totalamount;
TargetSales = targetsales;
Currency = currency;
}
}

10. Now we have Custom Entities for Overview Page, CDS Views for Overview Page and Table Maintenance and Filter Custom Entity for Overview Page. Lets expose everything in Service Definition now. 

@EndUserText.label: ‘Overview page’
define service ZSER_DE_OVP {
expose ZCE_FilterEntity;
expose ZCE_RecentOrders;
expose ZCE_FINAL_DIV_TAX;
expose ZCE_SALES_BAR_LISTCARD;
expose ZCE_REGION_KPI;
expose ZCDS_REGION_ORDERS;
expose ZCDS_OVP_TABLE_MAINTAIN;
}

11. Create V2 Service Binding on Top of Service Definition as below:

Business Logic class for Custom Entity:

1. The Entities and pre-requisites are done to create overview page. Now lets deep dive into the main part which is Business Logic class. on top of all the custom Entities the below annotation has been given to link the custom entity logic to a business logic class. 

@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’

2. The logic for ZCL_OVP_RAP_QUERY is as below. 

Note: This class carries logic for value help entity and overview page card entities except ZCDS_REGION_ORDERS. Respective Page size, offset, max row calculations etc were precisely done. These calculations are important to give correct data in the output when the card size is reshaped, dragged down or up by user and also upon the pagination. The backend entity will dynamically push records to the front-end overview dashboard upon user interaction. 

a. Business class definition, Implementation and Pre-requisites common for all the cards:

CLASS zcl_ovp_rap_query DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_query_provider.
ENDCLASS.

CLASS zcl_ovp_rap_query IMPLEMENTATION.
METHOD if_rap_query_provider~select.

DATA: lt_ranges TYPE TABLE OF if_rap_query_filter=>ty_name_range_pairs,
lt_region TYPE RANGE OF ZCE_FilterEntity-Region,
lt_vh_regions TYPE TABLE OF ZCE_VH_Region.

DATA(lv_entity_id) = io_request->get_entity_id( ).
DATA(lt_sort_elements) = io_request->get_sort_elements( ).
DATA(lv_page_size) = io_request->get_paging( )->get_page_size( ).
DATA(lv_offset) = io_request->get_paging( )->get_offset( ).
DATA(lv_max_rows) =
COND #( WHEN lv_page_size = if_rap_query_paging=>page_size_unlimited
THEN 0
ELSE lv_page_size ).

TRY.
lt_ranges = io_request->get_filter( )->get_as_ranges( ).
CATCH cx_rap_query_filter_no_range.
ENDTRY.

IF lt_ranges IS NOT INITIAL.
DATA(region_value) = VALUE #( lt_ranges[ 1 ]-range ).
lt_region = VALUE #( FOR <ls_ranges> IN region_value
( CORRESPONDING #( <ls_ranges> ) ) ).
SELECT * FROM zovp_analytics
WHERE region IN _region
INTO TABLE (lt_ovp_analytics).
ELSE.
SELECT * FROM zovp_analytics
INTO TABLE _ovp_analytics.
ENDIF.

b. Logic for Value Help for Overview page

IF lv_entity_id = ‘ZCE_VH_REGION’.
lt_vh_regions = VALUE #( ( Region = ‘North’ ) ( Region = ‘South’ ) ).
io_response->set_total_number_of_records( lines( lt_vh_regions ) ).
io_response->set_data( lt_vh_regions ).

c. Logic for List Card – Recent Orders 

ELSEIF lv_entity_id = ‘ZCE_RECENTORDERS’.
DATA: lt_orders TYPE TABLE OF ZCE_RecentOrders,
lt_ordered_output TYPE TABLE OF ZCE_RecentOrders,
lv_sum TYPE ZCE_RecentOrders-OrderAmount.

LOOP AT lt_ovp_analytics ASSIGNING FIELD-SYMBOL(<ls_ro>)
GROUP BY ( customer = <ls_ro>-customer ) INTO DATA(ls_group).
DATA(lv_count) = 0.
lv_sum = 0.
DATA(lv_status) = VALUE string( ).
LOOP AT GROUP ls_group ASSIGNING FIELD-SYMBOL(<ls_member>).
lv_count += 1.
lv_sum += <ls_member>-totalamount.
lv_status = <ls_member>-status.
ENDLOOP.
IF lv_count > 1.
lv_sum = lv_sum * ‘0.9’.
ENDIF.

lt_orders = VALUE #( BASE lt_orders (
Customer = ls_group-customer
Status = lv_status
statuscolor =
COND #( WHEN lv_status = ‘Delivered’
THEN 3
WHEN lv_status = ‘Pending’
THEN 2
WHEN lv_status = ‘Arrival’
THEN 0
ELSE 1 )
orderamount = lv_sum
Currency = ‘USD’
) ).

ENDLOOP.
SORT lt_orders BY Customer.
lt_ordered_output =
VALUE #( FOR <ls_data> IN lt_orders FROM lv_offset TO lv_max_rows
( CORRESPONDING #( <ls_data> ) ) ).

io_response->set_total_number_of_records( lines( lt_orders ) ).
io_response->set_data( lt_orders ).

d. Logic for List Bar Card – Sales Performance

ELSEIF lv_entity_id = ‘ZCE_SALES_BAR_LISTCARD’.
DATA: lt_sb_card TYPE TABLE OF zcds_sales_bar_listcard,
lt_final_sb TYPE TABLE OF zcds_sales_bar_listcard.

SELECT
COUNT(*) AS TotalOrders
FROM _ovp_analytics AS a
INTO (lv_order_count).
IF sy-subrc = 0.
SELECT
CAST( ‘0’ AS INT4 ) AS TotalOrders,
Status,
SUM( totalamount ) AS TotalSales,
SUM( targetsales ) AS TargetSales,
CAST( ‘3’ AS INT1 ) AS StatusColour,
currency
FROM _ovp_analytics AS a
GROUP BY Status, currency
INTO TABLE _sb_card.
IF sy-subrc = 0.

lt_final_sb =
VALUE #( FOR <ls_sb> IN lt_sb_card FROM lv_offset TO lv_max_rows
( TotalOrders = lv_order_count
status = <ls_sb>-status
TotalSales = <ls_sb>-TotalSales
TargetSales = <ls_sb>-TargetSales
StatusColor = <ls_sb>-StatusColor
Currency = <ls_sb>-Currency ) ).

io_response->set_total_number_of_records( lines( lt_final_sb ) ).
io_response->set_data( lt_final_sb ).
ENDIF.
ENDIF.

e. Logic for Table card with KPI Header – Tax Per Division

ELSEIF lv_entity_id = ‘ZCE_FINAL_DIV_TAX’.
DATA:lt_div TYPE TABLE OF zcds_final_div_tax,
lt_final_div TYPE TABLE OF zcds_final_div_tax.

SELECT
division,
CAST( AVG( tax ) * 100 AS DEC ) AS TaxAverage,
CAST( ‘3’ AS INT1 ) AS StatusColor
FROM _ovp_analytics AS a
GROUP BY division
INTO TABLE (lt_tax1).
IF sy-subrc = 0.
SELECT
AVG( taxaverage ) AS taxaverage
FROM _tax1 AS a
INTO TABLE (lt_tax2).
IF sy-subrc = 0.
SELECT a~division,
a~taxaverage AS TaxAverage,
b~taxaverage AS globaltaxaverage
FROM _tax1 AS a
CROSS JOIN _tax2 AS b
INTO TABLE _div.
ENDIF.
ENDIF.

lt_final_div =
VALUE #( FOR <ls_div> IN lt_div FROM lv_offset TO lv_max_rows
( CORRESPONDING #( <ls_div> ) ) ).

io_response->set_total_number_of_records( lines( lt_final_div ) ).
io_response->set_data( lt_final_div ).

f. Logic for KPI Card – Region Order Sales

ELSEIF lv_entity_id = ‘ZCE_REGION_KPI’.
DATA:lt_region_kpi TYPE TABLE OF zce_region_kpi.

SELECT sum( totalamount ) as TotalAmount
FROM @LT_ovp_analytics as a
INTO TABLE @DATA(lt_kpi_1).
IF sy-subrc = 0.
lt_region_kpi = VALUE #( FOR <ls_kpi_1> IN lt_kpi_1
( TotalAmount = <ls_kpi_1>-totalamount
Currency = ‘USD’ ) ).

io_response->set_total_number_of_records( lines( lt_region_kpi ) ).
io_response->set_data( lt_region_kpi ).

ENDIF.
ENDMETHOD.
ENDCLASS.

Henceforth the Backend is ready with all the cards and its corresponding logic. Front End can be started with this and at the end I will share some real time business/project scenarios and add additional entities for that as well. 

 FRONT END:

 Create New Project with Overview Page Template

1. Open BAS Dev space and click -> Create Project from Template.

2. Select SAP Fiori Generator and select Overview page Template.

3. Select ABAP Cloud Foundry Environment and give RAP Service as Data source.

4. Select Main Filter Entity for Overview Page.

5. Give Project Attributes and Select Deployment and Fiori launch pad configuration as YES. 

6. Choose Target as cloud foundry ( Since we are doing in BTP ) and select Destination.

7. Define Semantic object and Action as below and then click Finish.

Note: This is important for Deployment. 

8. The new Project will be created and in manifest Filter entity that we have given is auto-configured as below.

9. Now in order to make the cards to be created in overview page, Under cards{} of array “sap.ovp” we need to configure the backend entity one by one as below.

a. Manifest JSON for List Card – Recent Orders 

“recentOrderCard”: {
“template”: “sap.ovp.cards.list”,
“model”: “mainModel”,
“settings”: {
“title”: “Recent Orders”,
“subTitle”: “Recent Orders By Customer”,
“entitySet”: “ZCE_RecentOrders”,
“annotationPath”: “com.sap.vocabularies.UI.v1.LineItem”
}
},

b. Manifest JSON for List Bar Card – Sales Performance

“card_SalesBarChart”: {
“template”: “sap.ovp.cards.list”,
“model”: “mainModel”,
“settings”: {
“title”: “Sales Performance”,
“entitySet”: “ZCDS_SALES_BAR_LISTCARD”,
“listFlavor”: “bar”,
“listType”: “extended”,
“annotationPath”: “com.sap.vocabularies.UI.v1.LineItem”,
“dataPointAnnotationPath”: “com.sap.vocabularies.UI.v1.DataPoint#CountKPI”,
“addODataSelect”: true,
“valueFormat”: {
“numberOfFractionalDigits”: 2,
“style”: “short”
}
}
}

c. Manifest JSON for Table card with KPI Header – Tax Per Division

“divisionByTax”: {
“template”: “sap.ovp.cards.table”,
“model”: “mainModel”,
“settings”: {
“title”: “Tax per Division”,
“subTitle”: “Average Tax”,
“entitySet”: “ZCDS_FINAL_DIV_TAX”,
“annotationPath”: “com.sap.vocabularies.UI.v1.LineItem”,
“dataPointAnnotationPath”: “com.sap.vocabularies.UI.v1.DataPoint#SalesData”,
“addODataSelect”: true
}
}

d. Manifest JSON for KPI Card – Region Order Sales

“RegionKPI”: {
“template”: “sap.ovp.cards.charts.analytical”,
“model”: “mainModel”,
“settings”: {
“entitySet”: “ZCE_REGION_KPI”,
“title”: “Region – Order Sales”,
“selectionPresentationAnnotationPath”: “com.sap.vocabularies.UI.v1.SelectionPresentationVariant#KPIRegion”,
“dataPointAnnotationPath”: “com.sap.vocabularies.UI.v1.DataPoint#KPIRegion”
}
}

e. Manifest JSON for Table card – Sales By Region

“salesByRegionCard”: {
“template”: “sap.ovp.cards.table”,
“model”: “mainModel”,
“settings”: {
“title”: “Sales by Region”,
“subTitle”: “Northern vs Southern”,
“entitySet”: “ZCDS_REGION_ORDERS”,
“annotationPath”: “com.sap.vocabularies.UI.v1.LineItem”
}
}

OUTPUT:

OUTPUT with Filter:

Note:  I’ve also configured list report for Table Maintanence with CRUD Operations as below in BAS. Created seperate project for it. Not adding all steps here because it will deviate the overview page concepts that is been explained.  In upcoming blogs we can use this list report for intent based navigation from overview page. 

 

REAL TIME BUSINESS SCENARIOS:

SCENARIO 1:

Problem: Consider scenario where Business is expecting to reuse the existing standard entity/CDS View which does not have necessary annotations from backend to build overview page. In this case the annotations to be done from Front end. But being a fresher to front end we cannot directly jump into XML Annotations, right? 

Solution: For beginners only, BAS has given an option called Fiori Open Guided Development. 

Build:

Backend:

Create CDS View as below from backend with feeble annotations from backend. The idea is to do the annotations from front end for line item, identification etc. In below code there is only @ui.lineitem annotation and not labels/data point etc. 

 

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Region Customer’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZCDS_REGION_CUSTOMERS
as select from zovp_analytics
{
@UI.lineItem: [{ position: 10 }]
key region as Region,
@UI.lineItem: [{ position: 20 }]
key customer as customer,
@UI.lineItem: [{ position: 30 }]
@Semantics.amount.currencyCode: ‘Currency’
sum(totalamount) as OrderAmount,
currency
} group by region, customer, currency;

 2. Expose the CDS View entity to existing RAP Service Definition. 

@EndUserText.label: ‘Overview page’
define service ZSER_DE_OVP {
// not mentioning all existing entities again
expose ZCDS_REGION_CUSTOMERS;
}

3. Refresh the Service Binding. 

Frontend:

1. Now navigate to the BAS Project. click Ctrl + shift + P and enter Fiori: Open Guided Development.

2. We can create simple Table card for this and henceforth pick the option Table card and select start guide.

3. On each step the screen will be developer interactive, we can select the necessary details and click on Add XML Annotation. In step 1 select Model, Entity Type and Entity Type Property then select add XML Annotation.

4. In step 2, the screen is for UI.lineitem generation, Henceforth select the Model, line item qualifier, entity details and under Add data field section, select the necessary properties from the entity to be displayed as Table card and label them from front end. After completing everything select add insert snippet.

5. Similarly Step 3 is for UI.Selection variant and Step 4 is for UI.Identification, if any of these properties are required then modify these screens and insert snippets accordingly. For this table card its not required so I just selected model and left empty.

6. In step 5 select Datapoint and define qualifers if needed(usually if KPI Header is required in table card then this is required) and then specify final qualifiers that has been selected for all.

7. Once everything is done check your manifest.json and annotations.xml. Below collage shows the auto generated code from fiori open guided development in manifest and xml annotations. from this way being a backend developer, we can automate front end annotations and link the qualifiers.

Output:

SCENARIO 2:

Problem: Now let’s have a scenario where custom overview page is being build and in that for one of the entity, business is asking to refer the different RAP service or in real time may be standard overview page service. so till now our custom overview page is having several cards configured in manifest and all are dependent to one single service. so, the problem is to incorporate/add entity from different RAP service.

Solution: Since the manifest model and service is directing towards single parent service and it’s filter entity, we have to add another service in the same overview page project and consume that service in manifest. 

Build:

BACKEND:

1. Let’s create Below CDS View Entity – ZCE_REGION_TARGET

@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
@EndUserText.label: ‘Region Target KPI’
@Metadata.allowExtensions: true
@UI.presentationVariant: [{ qualifier: ‘RegionTarget’, text: ‘KPI: Total Region Sales’,
visualizations: [{type: #AS_DATAPOINT, element: ‘TargetAmount’, qualifier: ‘TotalAmount’ }] }]
@UI.selectionVariant: [{ qualifier: ‘RegionTarget’, text: ‘KPI: Total Region Sales’ }]
@UI.selectionPresentationVariant: [{ qualifier: ‘RegionTarget’, presentationVariantQualifier: ‘RegionTarget’, selectionVariantQualifier: ‘RegionTarget’ }]
define root custom entity ZCE_REGION_TARGET
{
@Semantics.amount.currencyCode : ‘Currency’
@UI.kpi: [{ detail: {
defaultPresentationVariantQualifier: ‘RegionTarget’
} }]
@ui.dataPoint: {
description: ‘Total | USD’,
qualifier: ‘RegionTarget’ }
key TargetAmount : abap.curr(15,2);
Currency : abap.cuky;
Region : abap.char(20);
}

2. Expose this CDS View entity to a new RAP Service Definition

@EndUserText.label: ‘Overviewpage service 2’
define service ZSER_DE_OVP_2 {
expose ZCE_REGION_TARGET;
}

3. Create Service Binding – ZSB_OVP_2_RAP_SRV_V2 as below and publish it. Copy the service URL as highlighted below and keep it ready.

FRONT END:

1. Click ctrl + shift + p and select Fiori: Open Service Manager . In service Manager as shown below existing service is already available. Now click on Add service.

2. Now select the Destination, BTP Credentials and paste the service URL that has been copied from RAP Service Binding.

3. Once the Add button is clicked, the new service will be automatically added to the existing overview page project as below.

4. This Automation reduces 90% of work. Now if we navigate to Overview page project, new service has been enabled in service folder as shown below.

5. The Manifest also gets auto-generated with new service detail and it’s model. 

6. Now under cards section, add this logic in manifest with appropriate second service model detail. This piece of JSON will add new KPI card to the existing overview page from the secondary/Alien service which is not dependent to existing cards. 

“RegionTarget”: {
“template”: “sap.ovp.cards.charts.analytical”,
“model”: “ZSB_OVP_2_RAP_SRV_V2”,
“settings”: {
“entitySet”: “ZCE_REGION_TARGET”,
“title”: “Region – Target Sales”,
“selectionPresentationAnnotationPath”: “com.sap.vocabularies.UI.v1.SelectionPresentationVariant#RegionTarget”,
“dataPointAnnotationPath”: “com.sap.vocabularies.UI.v1.DataPoint#RegionTarget”
}
}
},

OUTPUT:

Hence the Target sales KPI Card is added from different service as highlighted above.

CONCLUSION:

I believe this blog collectively adds more detail on Overview page creation with different card add-ons from both backend and front end. Also from the real-time business scenarios we got to know how to add overview cards from Fiori Open Guided Development and accessing different service using Fiori Open service manager.

In future blogs, lets see intent based navigation and deployment of these apps to Cloud HTML5 Apps and SAP Build workzone. 

Overall Output Video:

 

​ Overview page is generally used for grouping Embedded Analytics upon different dimension which flows towards individual entities. But, Have you ever wondered on creating overview page with RAP on Backend. This blog will explain on the same in detail. 
Overview for creating Overview page – Dashboard:
We are going with complete end-end cloud ready approach and for the same I’ve used BTP Cloud foundry Environment for this Dashboard creation. Below are the sequential steps to create overview page Dashboard. 
Backend:
1. Set up BTP Cloud foundry Environment and enable it in Eclipse ADT.
2. Create Individual Entities using RAP Custom Entity/CDS View. 
3. Create Filter Entity which will be enabled as common live Filter
4. Create Business logic class for these Custom Entities.
Front End:
1. Create New Project with Overview Page Template
2. Link the Entities in BAS and create different cards as per the requirement.
3. Dry run the app to debug and check backend and front end is interlinked and fiori element is configured properly. 
Below Flow chart explains the complete process precisely.

Now let’s start the Development. 
 Set up BTP Cloud foundry Environment and enable it in Eclipse ADT
1. Login to your BTP Cockpit as below. 

2. Click Boosters from the left-hand navigation pane of your Global Account view and select prepare an Account for ABAP Trail.

3. The system automatically assigns roles, establishes the Cloud Foundry runtime, builds a dev space, and spins up the ABAP service instance.Once complete, skip down to the Generate a Service Key. Download the service key once the process is completed and store it locally.

4. Now navigate to SubAccount, Instances and Subscriptions and locate the ABAP instance. Right click on 3 dots to create service key.

5. Once service key is created, click on it and navigate to the JSON that it shows. Now, copy the JSON and navigate to Eclipse.

6. Now in Eclipse create new project and enter the JSON that has been copied from BTP Cockpit. This JSON will create the cloud project in Eclipse. 

Create Individual Entities using RAP Custom Entity/CDS View
1. Since BTP Cockpit in Trail does not have valid SAP Predefined Tables, Let’s create the custom ZTable – ZOVP_ANALYTICS as below.
 
@EndUserText.label : ‘Table for analytics’
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #ALLOWED
define table zovp_analytics {

key client_id : abap.clnt not null;
key orderid : abap.char(10) not null;
region : abap.char(20) not null;
division : abap.char(5);
districhl : abap.char(5);
customer : abap.char(10);
status : abap.char(20);
order_del : abap.int4;
tax : abap.dec(10,3);
@Semantics.amount.currencyCode : ‘zovp_analytics.currency’
totalamount : abap.curr(15,2);
@Semantics.amount.currencyCode : ‘zovp_analytics.currency’
targetsales : abap.curr(15,2);
currency : abap.cuky;

}
Note: Before getting into next steps, let’s have a quick overview on RAP Custom Entity. RAP Custom Entity is built on RAP Query Provider Interface which will collectively carry the logic for the desired Entity Type. This can be directly given as entity in service definition and also can be exposed in service binding.
2. Now for Overview Page Filter, lets create  Separate  RAP Custom Entity with Region as Filter. Note the case sensitive in Filter Entity and Child Entities should be same and with that only $filter will correctly map the appropriate filter while triggering target entities after entering filter value. 
@EndUserText.label: ‘Global Filter Entity’
@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
define root custom entity ZCE_FilterEntity
{
.selectionField: [{ position: 10 }]
@Consumption.valueHelpDefinition: [{ entity: { name: ‘ZCE_VH_Region’, element: ‘Region’ } }]
@EndUserText.label: ‘Region’
key Region : tcode;
}
3. Let’s Create backend RAP Custom Entity – ZCE_RecentOrders for the first Table card for our overview page. In this card the grouping Analytics happens upon Customer, and it will work on Region Filter as well. 
Note:

Before going further let’s understand how this works. As mentioned earlier, overview page is for Embedded Analytics calculation on different metrics. so, if Region is passed in filter, then from the custom entity class first the overall table will be filtered with Region and then the grouping metrics along with aggregation data lone will be taken in another select query and grouped for output. 
We are giving Annotation in each custom entity as the same direction path will be configured in Front end Manifest which will reflect in the overview card. 

@EndUserText.label: ‘Recent Orders for Table Card’
@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
define root custom entity ZCE_RecentOrders
{
.lineItem: [{ position: 10, label: ‘Customer’ }]
key Customer : abap.char(40);

.lineItem: [{ position: 20, label: ‘Status’ }]
Status : abap.char(10);

.hidden : true
StatusColor : abap.int1;

.lineItem: [{ position: 30, label: ‘Amount’ }]
@Semantics.amount.currencyCode: ‘Currency’
OrderAmount : abap.curr(15,2);
.hidden : true
Currency : abap.cuky;
.hidden : true
Region : abap.char(20);
}
4. The custom entity – ZCE_SALES_BAR_LIST_CARD is created for second card and this will be configured as Bar list card template in Overview page. This card has calculative metrics on source and target and for that datapoint annotations were also done. 
@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
@EndUserText.label: ‘Sales Bar Chart List’
define root custom entity ZCE_SALES_BAR_LISTCARD
{
.dataPoint: { title: ‘Order Count’, description: ‘Total Orders’, qualifier: ‘CountKPI’ }
key TotalOrders : abap.int4;
.lineItem: [{ position: 10, label: ‘Status’ }]
key status: abap.char(20);
.lineItem: [{ position: 20, type: #AS_DATAPOINT }]
@Semantics.amount.currencyCode: ‘Currency’
.dataPoint: {
visualization: #PROGRESS,
targetValue: 1000,
criticality: ‘StatusColor’
}
TotalSales : abap.curr(15,2);
.lineItem: [{ position: 30, type: #AS_DATAPOINT, label: ‘Target Value’ }]
.dataPoint: {
title: ‘Target’
}
@Semantics.amount.currencyCode: ‘Currency’
TargetSales : abap.curr(15,2);
StatusColor : abap.int1;
Currency : abap.cuky;
.hidden : true
Region : abap.char(20);
}
5. The custom entity – ZCE_FINAL_DIV_TAX is created for third card and this will be configured as list card template in Overview page. In this card the grouping happens on Division and in addition it will have KPI Header which will give overall Tax Average for the respective filter. Datapoint Annotation is configured for KPI header. 
@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
@EndUserText.label: ‘Tax Average’
define root custom entity ZCE_FINAL_DIV_TAX
{
.lineItem: [{ position: 10, label: ‘Division’ }]
key Division : abap.char(5);
.lineItem: [{ position: 20, label: ‘Tax %’ }]
TaxAverage : abap.dec(15,2);
.dataPoint: { qualifier: ‘SalesData’,
title: ‘Tax Average’,
criticality: ‘StatusColor’,
valueFormat.numberOfFractionalDigits: 2 }
GlobalTaxAverage : abap.dec(15,2);
StatusColor : int1;
.hidden : true
Region : abap.char(20);
}
 6. Now lets create CDS View Entity – ZCDS_REGION_ORDERS instead of RAP Custom Entity for Region card which will be configured as 4th overview card with Table card template. 
Why CDS View for this card ? – Because this card has property Region which will work directly once the Overview filter Region is entered. URL will intrepret $filter Region to this CDS view and since this view has Region Property built within itself and henceforth it can be filtered easily. 
Note: Instead of custom Entity, we can also use CDS with parameters in backend but that will support only if the CDS View is Analytical CDS. But unfortunately RAP V2 Does not support Analytical CDS View. CDS with Analytical is supported in RAP with OData V4, but again unfortunately OData V4 is not supported in BTP Overview page template as of today. So, these are the challenges I faced and to avoid more complications I went with custom entity for the other cards. 
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Orders Delivered by Region’
@Metadata.ignorePropagatedAnnotations: false
define root view entity ZCDS_REGION_ORDERS
as select from zovp_analytics
{
@UI.lineItem: [{ position: 10, label: ‘Region’ }]
key region as Region,
@UI.lineItem: [{ position: 20, label: ‘Orders Delivered’ }]
sum(order_del) as OrdersDelivered,
@UI.lineItem: [{ position: 30, label: ‘Order Amount’ }]
@Semantics.amount.currencyCode: ‘Currency’
sum(totalamount) as OrderAmount,
currency
} group by region, currency;
7. Till now we created Custom Entities for Table and list card. For KPI card, we can create Custom Entity now. In this card Total sales happened for all region would be collectively calculated.
Note: Since this is KPI card, from front end we may choose Analytical card template to show KPI data alone and from backend we must have selection variant and data point annotations. 
@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
@EndUserText.label: ‘Region KPI’
@Metadata.allowExtensions: true
@UI.presentationVariant: [{ qualifier: ‘KPIRegion’,
text: ‘KPI: Total Region Sales’,
visualizations: [{type: #AS_DATAPOINT,
element: ‘TotalAmount’,
qualifier: ‘TotalAmount’ }] }]
@UI.selectionVariant: [{ qualifier: ‘KPIRegion’,
text: ‘KPI: Total Region Sales’ }]
@UI.selectionPresentationVariant: [{ qualifier: ‘KPIRegion’,
presentationVariantQualifier: ‘KPIRegion’,
selectionVariantQualifier: ‘KPIRegion’ }]
define root custom entity ZCE_REGION_KPI
{
@Semantics.amount.currencyCode : ‘Currency’
@UI.kpi: [{ detail: {
defaultPresentationVariantQualifier: ‘KPIRegion’
} }]
@ui.dataPoint: {
description: ‘Total | USD’,
qualifier: ‘KPIRegion’ }
key TotalAmount : abap.curr(15,2);
Currency : abap.cuky;
Region : abap.char(20);
}
8. So, we have custom entity with 4 cards now. To populate data to custom table ZOVP_ANALYTICS, we can create one separate CDS View entity – ZCDS_OVP_TABLE_MAINTAIN from which list report can be created.  
Note: This CDS View Entity alone will be configured as list report from BAS for data maintanence purpose and it can be used for INTENT BASED NAVIGATION as well in future. 
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘TABLE MAINTAIN’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZCDS_OVP_TABLE_MAINTAIN
as select from zovp_analytics
{
.facet: [
{ id: ‘GeneralInfo’,
type: #COLLECTION,
label: ‘General Information’,
position: 10 },
{ id: ‘OrderDetails’,
parentId: ‘GeneralInfo’,
type: #IDENTIFICATION_REFERENCE,
label: ‘Order Details’,
position: 10 }
]
.lineItem: [{ position: 10 }]
.identification: [{ position: 10, label: ‘Order ID’ }]
key orderid as Orderid,
.lineItem: [{ position: 20 }]
.identification: [{ position: 20, label: ‘Region’ }]
.selectionField: [{position: 10 }]
@Consumption.valueHelpDefinition: [{ entity: { name: ‘ZCE_VH_Region’, element: ‘Region’ } }]
region as Region,
.lineItem: [{ position: 30 }]
.identification: [{ position: 30, label: ‘Division’ }]
division as Division,
.lineItem: [{ position: 40 }]
.identification: [{ position: 40, label: ‘Distribution channel’ }]
districhl as Districhl,
.lineItem: [{ position: 50 }]
.identification: [{ position: 50, label: ‘Customer’ }]
customer as Customer,
.lineItem: [{ position: 60 }]
.identification: [{ position: 60, label: ‘Status’ }]
status as Status,
.lineItem: [{ position: 70 }]
.identification: [{ position: 70, label: ‘Orders Delivered’ }]
order_del as OrderDel,
.lineItem: [{ position: 80 }]
.identification: [{ position: 80, label: ‘Tax’ }]
tax as Tax,
.lineItem: [{ position: 90 }]
.identification: [{ position: 90, label: ‘Total Amount’ }]
@Semantics.amount.currencyCode : ‘currency’
totalamount as Totalamount,
.lineItem: [{ position: 100 }]
.identification: [{ position: 100, label: ‘Target Sales’ }]
@Semantics.amount.currencyCode : ‘currency’
targetsales as Targetsales,
.lineItem: [{ position: 110 }]
.identification: [{ position: 110, label: ‘Currency’ }]
currency as Currency
}
9. Create Managed Behaviour Definition to enable CRUD Operations for ZCDS_OVP_TABLE_MAINTAIN. (This Behavior Definition is not linked to Overview page. It is solely created for List report which is just to populate data to main table. If the intent is to create overview page only then probably this can be avoided ). 
managed implementation in class zbp_cds_ovp_table_maintain unique;
strict ( 2 );

define behavior for ZCDS_OVP_TABLE_MAINTAIN alias ovp_table_maintain
persistent table zovp_analytics
lock master
authorization master ( instance )
{
create ( authorization : global );
update;
delete;

mapping for zovp_analytics
{
Orderid = orderid;
Region = region;
Division = division;
Districhl = districhl;
Customer = customer;
Status = status;
OrderDel = order_del;
Tax = tax;
TotalAmount = totalamount;
TargetSales = targetsales;
Currency = currency;
}
}
10. Now we have Custom Entities for Overview Page, CDS Views for Overview Page and Table Maintenance and Filter Custom Entity for Overview Page. Lets expose everything in Service Definition now. 
@EndUserText.label: ‘Overview page’
define service ZSER_DE_OVP {
expose ZCE_FilterEntity;
expose ZCE_RecentOrders;
expose ZCE_FINAL_DIV_TAX;
expose ZCE_SALES_BAR_LISTCARD;
expose ZCE_REGION_KPI;
expose ZCDS_REGION_ORDERS;
expose ZCDS_OVP_TABLE_MAINTAIN;
}
11. Create V2 Service Binding on Top of Service Definition as below:

Business Logic class for Custom Entity:
1. The Entities and pre-requisites are done to create overview page. Now lets deep dive into the main part which is Business Logic class. on top of all the custom Entities the below annotation has been given to link the custom entity logic to a business logic class. 
@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
2. The logic for ZCL_OVP_RAP_QUERY is as below. 
Note: This class carries logic for value help entity and overview page card entities except ZCDS_REGION_ORDERS. Respective Page size, offset, max row calculations etc were precisely done. These calculations are important to give correct data in the output when the card size is reshaped, dragged down or up by user and also upon the pagination. The backend entity will dynamically push records to the front-end overview dashboard upon user interaction. 
a. Business class definition, Implementation and Pre-requisites common for all the cards:
CLASS zcl_ovp_rap_query DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_query_provider.
ENDCLASS.

CLASS zcl_ovp_rap_query IMPLEMENTATION.
METHOD if_rap_query_provider~select.

DATA: lt_ranges TYPE TABLE OF if_rap_query_filter=>ty_name_range_pairs,
lt_region TYPE RANGE OF ZCE_FilterEntity-Region,
lt_vh_regions TYPE TABLE OF ZCE_VH_Region.

DATA(lv_entity_id) = io_request->get_entity_id( ).
DATA(lt_sort_elements) = io_request->get_sort_elements( ).
DATA(lv_page_size) = io_request->get_paging( )->get_page_size( ).
DATA(lv_offset) = io_request->get_paging( )->get_offset( ).
DATA(lv_max_rows) =
COND #( WHEN lv_page_size = if_rap_query_paging=>page_size_unlimited
THEN 0
ELSE lv_page_size ).

TRY.
lt_ranges = io_request->get_filter( )->get_as_ranges( ).
CATCH cx_rap_query_filter_no_range.
ENDTRY.

IF lt_ranges IS NOT INITIAL.
DATA(region_value) = VALUE #( lt_ranges[ 1 ]-range ).
lt_region = VALUE #( FOR <ls_ranges> IN region_value
( CORRESPONDING #( <ls_ranges> ) ) ).
SELECT * FROM zovp_analytics
WHERE region IN _region
INTO TABLE (lt_ovp_analytics).
ELSE.
SELECT * FROM zovp_analytics
INTO TABLE _ovp_analytics.
ENDIF.
b. Logic for Value Help for Overview page
IF lv_entity_id = ‘ZCE_VH_REGION’.
lt_vh_regions = VALUE #( ( Region = ‘North’ ) ( Region = ‘South’ ) ).
io_response->set_total_number_of_records( lines( lt_vh_regions ) ).
io_response->set_data( lt_vh_regions ).
c. Logic for List Card – Recent Orders 
ELSEIF lv_entity_id = ‘ZCE_RECENTORDERS’.
DATA: lt_orders TYPE TABLE OF ZCE_RecentOrders,
lt_ordered_output TYPE TABLE OF ZCE_RecentOrders,
lv_sum TYPE ZCE_RecentOrders-OrderAmount.

LOOP AT lt_ovp_analytics ASSIGNING FIELD-SYMBOL(<ls_ro>)
GROUP BY ( customer = <ls_ro>-customer ) INTO DATA(ls_group).
DATA(lv_count) = 0.
lv_sum = 0.
DATA(lv_status) = VALUE string( ).
LOOP AT GROUP ls_group ASSIGNING FIELD-SYMBOL(<ls_member>).
lv_count += 1.
lv_sum += <ls_member>-totalamount.
lv_status = <ls_member>-status.
ENDLOOP.
IF lv_count > 1.
lv_sum = lv_sum * ‘0.9’.
ENDIF.

lt_orders = VALUE #( BASE lt_orders (
Customer = ls_group-customer
Status = lv_status
statuscolor =
COND #( WHEN lv_status = ‘Delivered’
THEN 3
WHEN lv_status = ‘Pending’
THEN 2
WHEN lv_status = ‘Arrival’
THEN 0
ELSE 1 )
orderamount = lv_sum
Currency = ‘USD’
) ).

ENDLOOP.
SORT lt_orders BY Customer.
lt_ordered_output =
VALUE #( FOR <ls_data> IN lt_orders FROM lv_offset TO lv_max_rows
( CORRESPONDING #( <ls_data> ) ) ).

io_response->set_total_number_of_records( lines( lt_orders ) ).
io_response->set_data( lt_orders ).
d. Logic for List Bar Card – Sales Performance
ELSEIF lv_entity_id = ‘ZCE_SALES_BAR_LISTCARD’.
DATA: lt_sb_card TYPE TABLE OF zcds_sales_bar_listcard,
lt_final_sb TYPE TABLE OF zcds_sales_bar_listcard.

SELECT
COUNT(*) AS TotalOrders
FROM _ovp_analytics AS a
INTO (lv_order_count).
IF sy-subrc = 0.
SELECT
CAST( ‘0’ AS INT4 ) AS TotalOrders,
Status,
SUM( totalamount ) AS TotalSales,
SUM( targetsales ) AS TargetSales,
CAST( ‘3’ AS INT1 ) AS StatusColour,
currency
FROM _ovp_analytics AS a
GROUP BY Status, currency
INTO TABLE _sb_card.
IF sy-subrc = 0.

lt_final_sb =
VALUE #( FOR <ls_sb> IN lt_sb_card FROM lv_offset TO lv_max_rows
( TotalOrders = lv_order_count
status = <ls_sb>-status
TotalSales = <ls_sb>-TotalSales
TargetSales = <ls_sb>-TargetSales
StatusColor = <ls_sb>-StatusColor
Currency = <ls_sb>-Currency ) ).

io_response->set_total_number_of_records( lines( lt_final_sb ) ).
io_response->set_data( lt_final_sb ).
ENDIF.
ENDIF.
e. Logic for Table card with KPI Header – Tax Per Division
ELSEIF lv_entity_id = ‘ZCE_FINAL_DIV_TAX’.
DATA:lt_div TYPE TABLE OF zcds_final_div_tax,
lt_final_div TYPE TABLE OF zcds_final_div_tax.

SELECT
division,
CAST( AVG( tax ) * 100 AS DEC ) AS TaxAverage,
CAST( ‘3’ AS INT1 ) AS StatusColor
FROM _ovp_analytics AS a
GROUP BY division
INTO TABLE (lt_tax1).
IF sy-subrc = 0.
SELECT
AVG( taxaverage ) AS taxaverage
FROM _tax1 AS a
INTO TABLE (lt_tax2).
IF sy-subrc = 0.
SELECT a~division,
a~taxaverage AS TaxAverage,
b~taxaverage AS globaltaxaverage
FROM _tax1 AS a
CROSS JOIN _tax2 AS b
INTO TABLE _div.
ENDIF.
ENDIF.

lt_final_div =
VALUE #( FOR <ls_div> IN lt_div FROM lv_offset TO lv_max_rows
( CORRESPONDING #( <ls_div> ) ) ).

io_response->set_total_number_of_records( lines( lt_final_div ) ).
io_response->set_data( lt_final_div ).
f. Logic for KPI Card – Region Order Sales
ELSEIF lv_entity_id = ‘ZCE_REGION_KPI’.
DATA:lt_region_kpi TYPE TABLE OF zce_region_kpi.

SELECT sum( totalamount ) as TotalAmount
FROM @LT_ovp_analytics as a
INTO TABLE @DATA(lt_kpi_1).
IF sy-subrc = 0.
lt_region_kpi = VALUE #( FOR <ls_kpi_1> IN lt_kpi_1
( TotalAmount = <ls_kpi_1>-totalamount
Currency = ‘USD’ ) ).

io_response->set_total_number_of_records( lines( lt_region_kpi ) ).
io_response->set_data( lt_region_kpi ).

ENDIF.
ENDMETHOD.
ENDCLASS.
Henceforth the Backend is ready with all the cards and its corresponding logic. Front End can be started with this and at the end I will share some real time business/project scenarios and add additional entities for that as well. 
 FRONT END:
 Create New Project with Overview Page Template
1. Open BAS Dev space and click -> Create Project from Template.

2. Select SAP Fiori Generator and select Overview page Template.

3. Select ABAP Cloud Foundry Environment and give RAP Service as Data source.

4. Select Main Filter Entity for Overview Page.

5. Give Project Attributes and Select Deployment and Fiori launch pad configuration as YES. 

6. Choose Target as cloud foundry ( Since we are doing in BTP ) and select Destination.

7. Define Semantic object and Action as below and then click Finish.
Note: This is important for Deployment. 

8. The new Project will be created and in manifest Filter entity that we have given is auto-configured as below.

9. Now in order to make the cards to be created in overview page, Under cards{} of array “sap.ovp” we need to configure the backend entity one by one as below.
a. Manifest JSON for List Card – Recent Orders 
“recentOrderCard”: {
“template”: “sap.ovp.cards.list”,
“model”: “mainModel”,
“settings”: {
“title”: “Recent Orders”,
“subTitle”: “Recent Orders By Customer”,
“entitySet”: “ZCE_RecentOrders”,
“annotationPath”: “com.sap.vocabularies.UI.v1.LineItem”
}
},
b. Manifest JSON for List Bar Card – Sales Performance
“card_SalesBarChart”: {
“template”: “sap.ovp.cards.list”,
“model”: “mainModel”,
“settings”: {
“title”: “Sales Performance”,
“entitySet”: “ZCDS_SALES_BAR_LISTCARD”,
“listFlavor”: “bar”,
“listType”: “extended”,
“annotationPath”: “com.sap.vocabularies.UI.v1.LineItem”,
“dataPointAnnotationPath”: “com.sap.vocabularies.UI.v1.DataPoint#CountKPI”,
“addODataSelect”: true,
“valueFormat”: {
“numberOfFractionalDigits”: 2,
“style”: “short”
}
}
}
c. Manifest JSON for Table card with KPI Header – Tax Per Division
“divisionByTax”: {
“template”: “sap.ovp.cards.table”,
“model”: “mainModel”,
“settings”: {
“title”: “Tax per Division”,
“subTitle”: “Average Tax”,
“entitySet”: “ZCDS_FINAL_DIV_TAX”,
“annotationPath”: “com.sap.vocabularies.UI.v1.LineItem”,
“dataPointAnnotationPath”: “com.sap.vocabularies.UI.v1.DataPoint#SalesData”,
“addODataSelect”: true
}
}
d. Manifest JSON for KPI Card – Region Order Sales
“RegionKPI”: {
“template”: “sap.ovp.cards.charts.analytical”,
“model”: “mainModel”,
“settings”: {
“entitySet”: “ZCE_REGION_KPI”,
“title”: “Region – Order Sales”,
“selectionPresentationAnnotationPath”: “com.sap.vocabularies.UI.v1.SelectionPresentationVariant#KPIRegion”,
“dataPointAnnotationPath”: “com.sap.vocabularies.UI.v1.DataPoint#KPIRegion”
}
}
e. Manifest JSON for Table card – Sales By Region
“salesByRegionCard”: {
“template”: “sap.ovp.cards.table”,
“model”: “mainModel”,
“settings”: {
“title”: “Sales by Region”,
“subTitle”: “Northern vs Southern”,
“entitySet”: “ZCDS_REGION_ORDERS”,
“annotationPath”: “com.sap.vocabularies.UI.v1.LineItem”
}
}
OUTPUT:

OUTPUT with Filter:

Note:  I’ve also configured list report for Table Maintanence with CRUD Operations as below in BAS. Created seperate project for it. Not adding all steps here because it will deviate the overview page concepts that is been explained.  In upcoming blogs we can use this list report for intent based navigation from overview page. 

 
REAL TIME BUSINESS SCENARIOS:
SCENARIO 1:
Problem: Consider scenario where Business is expecting to reuse the existing standard entity/CDS View which does not have necessary annotations from backend to build overview page. In this case the annotations to be done from Front end. But being a fresher to front end we cannot directly jump into XML Annotations, right? 
Solution: For beginners only, BAS has given an option called Fiori Open Guided Development. 
Build:
Backend:

Create CDS View as below from backend with feeble annotations from backend. The idea is to do the annotations from front end for line item, identification etc. In below code there is only @ui.lineitem annotation and not labels/data point etc. 

 
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: ‘Region Customer’
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZCDS_REGION_CUSTOMERS
as select from zovp_analytics
{
@UI.lineItem: [{ position: 10 }]
key region as Region,
@UI.lineItem: [{ position: 20 }]
key customer as customer,
@UI.lineItem: [{ position: 30 }]
@Semantics.amount.currencyCode: ‘Currency’
sum(totalamount) as OrderAmount,
currency
} group by region, customer, currency;
 2. Expose the CDS View entity to existing RAP Service Definition. 
@EndUserText.label: ‘Overview page’
define service ZSER_DE_OVP {
// not mentioning all existing entities again
expose ZCDS_REGION_CUSTOMERS;
}
3. Refresh the Service Binding. 
Frontend:
1. Now navigate to the BAS Project. click Ctrl + shift + P and enter Fiori: Open Guided Development.

2. We can create simple Table card for this and henceforth pick the option Table card and select start guide.

3. On each step the screen will be developer interactive, we can select the necessary details and click on Add XML Annotation. In step 1 select Model, Entity Type and Entity Type Property then select add XML Annotation.

4. In step 2, the screen is for UI.lineitem generation, Henceforth select the Model, line item qualifier, entity details and under Add data field section, select the necessary properties from the entity to be displayed as Table card and label them from front end. After completing everything select add insert snippet.

5. Similarly Step 3 is for UI.Selection variant and Step 4 is for UI.Identification, if any of these properties are required then modify these screens and insert snippets accordingly. For this table card its not required so I just selected model and left empty.

6. In step 5 select Datapoint and define qualifers if needed(usually if KPI Header is required in table card then this is required) and then specify final qualifiers that has been selected for all.

7. Once everything is done check your manifest.json and annotations.xml. Below collage shows the auto generated code from fiori open guided development in manifest and xml annotations. from this way being a backend developer, we can automate front end annotations and link the qualifiers.

Output:

SCENARIO 2:
Problem: Now let’s have a scenario where custom overview page is being build and in that for one of the entity, business is asking to refer the different RAP service or in real time may be standard overview page service. so till now our custom overview page is having several cards configured in manifest and all are dependent to one single service. so, the problem is to incorporate/add entity from different RAP service.
Solution: Since the manifest model and service is directing towards single parent service and it’s filter entity, we have to add another service in the same overview page project and consume that service in manifest. 
Build:
BACKEND:
1. Let’s create Below CDS View Entity – ZCE_REGION_TARGET
@ObjectModel.query.implementedBy: ‘ABAP:ZCL_OVP_RAP_QUERY’
@EndUserText.label: ‘Region Target KPI’
@Metadata.allowExtensions: true
@UI.presentationVariant: [{ qualifier: ‘RegionTarget’, text: ‘KPI: Total Region Sales’,
visualizations: [{type: #AS_DATAPOINT, element: ‘TargetAmount’, qualifier: ‘TotalAmount’ }] }]
@UI.selectionVariant: [{ qualifier: ‘RegionTarget’, text: ‘KPI: Total Region Sales’ }]
@UI.selectionPresentationVariant: [{ qualifier: ‘RegionTarget’, presentationVariantQualifier: ‘RegionTarget’, selectionVariantQualifier: ‘RegionTarget’ }]
define root custom entity ZCE_REGION_TARGET
{
@Semantics.amount.currencyCode : ‘Currency’
@UI.kpi: [{ detail: {
defaultPresentationVariantQualifier: ‘RegionTarget’
} }]
@ui.dataPoint: {
description: ‘Total | USD’,
qualifier: ‘RegionTarget’ }
key TargetAmount : abap.curr(15,2);
Currency : abap.cuky;
Region : abap.char(20);
}
2. Expose this CDS View entity to a new RAP Service Definition
@EndUserText.label: ‘Overviewpage service 2’
define service ZSER_DE_OVP_2 {
expose ZCE_REGION_TARGET;
}
3. Create Service Binding – ZSB_OVP_2_RAP_SRV_V2 as below and publish it. Copy the service URL as highlighted below and keep it ready.

FRONT END:
1. Click ctrl + shift + p and select Fiori: Open Service Manager . In service Manager as shown below existing service is already available. Now click on Add service.

2. Now select the Destination, BTP Credentials and paste the service URL that has been copied from RAP Service Binding.

3. Once the Add button is clicked, the new service will be automatically added to the existing overview page project as below.

4. This Automation reduces 90% of work. Now if we navigate to Overview page project, new service has been enabled in service folder as shown below.

5. The Manifest also gets auto-generated with new service detail and it’s model. 

6. Now under cards section, add this logic in manifest with appropriate second service model detail. This piece of JSON will add new KPI card to the existing overview page from the secondary/Alien service which is not dependent to existing cards. 
“RegionTarget”: {
“template”: “sap.ovp.cards.charts.analytical”,
“model”: “ZSB_OVP_2_RAP_SRV_V2”,
“settings”: {
“entitySet”: “ZCE_REGION_TARGET”,
“title”: “Region – Target Sales”,
“selectionPresentationAnnotationPath”: “com.sap.vocabularies.UI.v1.SelectionPresentationVariant#RegionTarget”,
“dataPointAnnotationPath”: “com.sap.vocabularies.UI.v1.DataPoint#RegionTarget”
}
}
},
OUTPUT:

Hence the Target sales KPI Card is added from different service as highlighted above.
CONCLUSION:
I believe this blog collectively adds more detail on Overview page creation with different card add-ons from both backend and front end. Also from the real-time business scenarios we got to know how to add overview cards from Fiori Open Guided Development and accessing different service using Fiori Open service manager.
In future blogs, lets see intent based navigation and deployment of these apps to Cloud HTML5 Apps and SAP Build workzone. 
Overall Output Video:   Read More Technology Blog Posts by Members articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author