Skip to Content
Technical Articles

CDS for Fiori My Inbox Generic Application

Problem statement: Today, a lot of customers are moving to Fiori launchpad for their day-to-day applications, there is a need to shift workflows also to Fiori My Inbox. Customers want to approve/reject their workitems from Fiori Inbox instead of conventional SBWP(SAP business Workplace). Fiori launchpad also offers a lot of new features such as Push notifications along with a better look and feel. Now say you have a custom workflow that needs to be upgraded to support approval from Fiori inbox or you have a requirement to build a new workflow in S/4Hana where approval will be done from Fiori MyInbox.
Let’s go back to our conventional approach: we used to have two options: Either create the User Decision step where the user will be presented with configured actions such as Approve/Reject or the second option where the user will be redirected to standard/custom transaction such as ME29N or ME54N from where action can be executed. In case you are using the User Decision step and customer asks for some more information, in that case, we used to copy that user decision step and change the description of that. But will that look good in Fiori launchpad?

SAP offers new Generic applications called My Inbox Generic application which can be easily built using Core Data services. We will try to build one such application from scratch. In a workflow, you can create a simple user Decision task and configure that task to a My Inbox generic application(built using CDS) to show data like below.

 

Clicking on items (* arrow on right) will take you to the object page containing item details.

 

Above application contains work items related to sales order, can be built using CDS and published OData Service. There is no need to build any UI5 Application for it. UI Application will be generated from annotations used in CDS.

Now let’s break this application down into elements.

Now we can start building CDS. In the above application, workitem is related to sales orders and we can divide the information into Header and item details. Header details coming from VBAK and item details from VBAP.

I already had a basic CDS on top of VBAK which I am going to use for this application. fetched all fields required for header data of my application.

@AbapCatalog.sqlViewName: 'ZI_BASE_VBAK'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Document Header'
define view ZI_BASIC_VBAK as select from vbak
  association [1..1] to I_Customer as _customer on _customer.Customer = $projection.KUNNR
 {
 vbeln as vbeln,
 erdat as ERDAT,
 auart as AUART,
 vkorg as VKORG,
 vtweg as VTWEG,
 spart as SPART,
 vkgrp as VKGRP,
 kunnr as KUNNR,
 _customer.CustomerName as CustomerName,
@Semantics.amount.currencyCode: 'WAERK'
 netwr as NETWR,
@Semantics.currencyCode: true  
 waerk as WAERK,
 _customer
 }

Now I am going to build a consumption view on top of it. If you want you can separate UI annotations from the projection/Consumption view using MDEs(Meta Data extensions). I am not going to do it as focus is on building My Inbox generic applications only.

 

First, take the header part, Header is containing the Sales order number and Customer Name.

Annotation @UI.headerInfo will be used for it. We need to specify fields from the element list used in CDS.

// This is make header portion of Workitem
@UI.headerInfo:{
    title.value: 'vbeln',
    description.value: 'CustomerName'
}
define view ZC_VBAK_CONS as select from ZI_BASIC_VBAK
association [1..*] to ZC_VBAP_CONS as _VBAPCONS on $projection.vbeln = _VBAPCONS.vbeln {

 

Now let’s focus on the Body of Workitem.

 

 

Body will be created using @UI.facet annotation. It can be subdivided into two sections: Header containing header details of sales order and Footer containing sales order items. Header can again be subdivided into multiple subgroups like in the above case Sales information, Customer Details, and Valuation. This division will be achieved using FieldGroup annotations.

define view ZC_VBAK_CONS as select from ZI_BASIC_VBAK
association [1..*] to ZC_VBAP_CONS as _VBAPCONS on $projection.vbeln = _VBAPCONS.vbeln {
@UI.facet: [
      // This is for the body
      {
        id: 'GeneralInformation',
        isSummary: true,
        type: #COLLECTION
       },
       // This is first subgroup 
       // of type Fieldgroup
       {
       parentId: 'GeneralInformation',
       id:'salesGroup',
       type: #FIELDGROUP_REFERENCE,
       targetQualifier: 'SalesDoc',
       label: 'Sales Information'
       },
       //Second Subgroup of type Fieldgroup
       {
       parentId: 'GeneralInformation',
       id:'customerGroup',
       type: #FIELDGROUP_REFERENCE,
       targetQualifier: 'CustomerInfo',
       label: 'Customer Details'
       },
       //Third Subgroup of type Fieldgroup
       { 
       parentId: 'GeneralInformation',
       id:'salesValue',
       type: #FIELDGROUP_REFERENCE,
       targetQualifier: 'SalesValue',
       label: 'Valuation'
       },
       // This is for the footer Items 
       //of type Lineitem 
       {
       id:'lineItems',
       type: #LINEITEM_REFERENCE,
       targetElement: '_VBAPCONS',
       label: 'Item Details'
       }]
      }

If you notice, the entire body of workitem is grouped under annotation @UI.Facet.

Facet will be having one ID, in this case, ‘GeneralInformation’. ‘isSummary: true‘ and type is mandatory for this. For facet, we would be using type as #COLLECTION, as this contains all subgroups. There are multiple types. For subgroups, we will be using #FIELDGROUP_REFERENCE (for header subgroups) and #LINEITEM_REFERENCE(for footer item table). For subgroups, parentID property is required to group into the main facet.

Each field group will be having a unique ID and a property ‘targetQualifier’, which will be used to identify which element belongs to which group.

Below is complete consumption view CDS. In the element list, we would be adding annotation¬†@UI.Fieldgroup having property ‘Qualifier‘ ( to identify the group to which it belongs ) and ‘Position‘ ( Position within that group ) for each element

@AbapCatalog.sqlViewName: 'ZCVBAKCONS'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Consumption View for VBAK'
@VDM.viewType: #CONSUMPTION
@OData.publish: true
// This is make header portion of Workitem
@UI.headerInfo:{
    title.value: 'vbeln',
    description.value: 'CustomerName'
}
define view ZC_VBAK_CONS as select from ZI_BASIC_VBAK
association [1..*] to ZC_VBAP_CONS as _VBAPCONS on $projection.vbeln = _VBAPCONS.vbeln {
@UI.facet: [
      // This is for the body
      {
        id: 'GeneralInformation',
        isSummary: true,
        type: #COLLECTION
       },
       // This is first subgroup 
       // of type Fieldgroup
       {
       parentId: 'GeneralInformation',
       id:'salesGroup',
       type: #FIELDGROUP_REFERENCE,
       targetQualifier: 'SalesDoc',
       label: 'Sales Information'
       },
       //Second Subgroup of type Fieldgroup
       {
       parentId: 'GeneralInformation',
       id:'customerGroup',
       type: #FIELDGROUP_REFERENCE,
       targetQualifier: 'CustomerInfo',
       label: 'Customer Details'
       },
       //Third Subgroup of type Fieldgroup
       { 
       parentId: 'GeneralInformation',
       id:'salesValue',
       type: #FIELDGROUP_REFERENCE,
       targetQualifier: 'SalesValue',
       label: 'Valuation'
       },
       // This is for the footer Items 
       //of type Lineitem 
       {
       id:'lineItems',
       type: #LINEITEM_REFERENCE,
       targetElement: '_VBAPCONS',
       label: 'Item Details'
       }]
  key vbeln as vbeln,
@UI.fieldGroup: [{ qualifier: 'SalesDoc', position: 10 }]  
@UI.identification: [{ position: 10 , label: 'Creation Date' }]
  ERDAT as ERDAT,
@UI.fieldGroup: [{ qualifier: 'SalesDoc', position: 20 }]    
@UI.identification: [{ position: 20 , label: 'Document Type' }]    
  AUART as AUART,
@UI.fieldGroup: [{ qualifier: 'SalesDoc', position: 30 }]  
@UI.identification: [{ position: 30 , label: 'Sales Org.' }]  
  VKORG as VKORG,
@UI.fieldGroup: [{ qualifier: 'SalesDoc', position: 40 }]  
@UI.identification: [{ position: 40 , label: 'Distribution Channel' }]  
  VTWEG as VTWEG,
@UI.fieldGroup: [{ qualifier: 'SalesDoc', position: 50 }]  
@UI.identification: [{ position: 50 , label: 'Division' }]  
  SPART as SPART,
@UI.fieldGroup: [{ qualifier: 'SalesDoc', position: 60 }]  
@UI.identification: [{ position: 60 , label: 'Sales Group' }] 
  VKGRP as VKGRP,
@UI.fieldGroup: [{ qualifier: 'CustomerInfo', position: 10 }]  
@UI.identification: [{ position: 70 , label: 'Customer' }] 
  KUNNR as KUNNR,
@UI.fieldGroup: [{ qualifier: 'CustomerInfo', position: 20 }]  
@UI.identification: [{ position: 80 ,label: 'Customer Name' }] 
  CustomerName as CustomerName,
@Semantics.amount.currencyCode: 'WAERK'
@UI.identification: [{ position: 90 ,label: 'Value' }] 
@UI.fieldGroup: [{ qualifier: 'SalesValue', position: 10 }] 
 NETWR as NETWR,
@Semantics.currencyCode: true  
@UI.identification: [{ position: 100 , label: 'Currency' }] 
@UI.fieldGroup: [{ qualifier: 'SalesValue', position: 20 }] 
 WAERK as WAERK,
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
  _VBAPCONS 
}

Now above CDS contains header details, the position of header items. Line item details (table at the footer) will be flowing from the associated CDS view. I have built a single CDS view for item details fetched from VBAP directly containing all UI semantics. If you want you can break it down into hierarchy and metadata extensions(for UI annotations).

In root CDS ZC_VBAK_CONS (above), I have defined One-to-Many association to ZC_VBAP_CONS view. Also marked it as Composition using @ObjectModel.associationType annotation at association exposure in element list. Compositions are a special kind of association used to build a Business object data model hierarchy. Instead of defining association to view containing item details, you can also define COMPOSITION directly.

If you notice in Facet annotation for LineItems, we have maintained a property ‘targetElement‘ instead of ‘targetQualifier‘. ‘targetElement’ points to exposed association directly. So UI semantics of the exposed entity will be used directly.

       {
       id:'lineItems',
       type: #LINEITEM_REFERENCE,
       targetElement: '_VBAPCONS',
       label: 'Item Details'
       }]

Once you have already defined Composition, navigation will be enabled automatically if the child entity contains something to display.

Below is the exposed child entity (CDS view ZC_VBAP_CONS). Again created a body of object page using @UI.Facet annotation. @UI.Fieldgroup annotation serves the same purpose as explained above for root entity.

@UI.Lineitem will be used to add columns/elements to the footer table. Position property will be used to order the columns.

@AbapCatalog.sqlViewName: 'ZCVBAPCONS'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Consumption View for VBAP'
@UI.headerInfo:{
  typeNamePlural: 'ItemDetials',
  typeName: 'Item Details'
}
define view ZC_VBAP_CONS as select from vbap 
association [1..1] to ZC_VBAK_CONS as _VBAKCONS on _VBAKCONS.vbeln = $projection.vbeln {
@UI.facet: [
      // This is for the body
      {
        id: 'GeneralInformation',
        isSummary: true,
        type: #COLLECTION
       },
       {
       parentId: 'GeneralInformation',
       id:'ItemDetails',
       type: #FIELDGROUP_REFERENCE,
       targetQualifier: 'ItemData',
       label: 'Sales Information'
       }]
 key vbeln as vbeln,
@UI.lineItem: [{position : 10 , label: 'Item',  importance: #HIGH }] 
@UI.fieldGroup: [{qualifier: 'ItemData', position: 10 }]
 key posnr as posnr,
 
@UI.lineItem: [{position : 20 , label: 'Description',  importance: #HIGH }] 
@UI.fieldGroup: [{qualifier: 'ItemData', position: 20 }]
 arktx as ARKTX,
 
@UI.lineItem: [{position : 30 , label: 'Material',  importance: #HIGH }] 
@UI.fieldGroup: [{qualifier: 'ItemData', position: 30 }]
 matnr as MATNR,
 
@UI.lineItem: [{position : 40 , label: 'Batch',  importance: #HIGH }] 
@UI.fieldGroup: [{qualifier: 'ItemData', position: 40 }]
 charg as CHARG,
 
@UI.lineItem: [{position : 50 , label: 'Mat. Group',  importance: #HIGH }]  
@UI.fieldGroup: [{qualifier: 'ItemData', position: 50 }]
 matkl as MATKL,
 
@UI.lineItem: [{position : 60 , label: 'Quantity',  importance: #HIGH }]  
@Semantics.quantity.unitOfMeasure: 'MEINS' 
@UI.fieldGroup: [{qualifier: 'ItemData', position: 60 }]
 zmeng as ZMENG,
 
@UI.lineItem: [{position : 70 , label: 'UOM',  importance: #HIGH }]  
@Semantics.unitOfMeasure: true
@UI.fieldGroup: [{qualifier: 'ItemData', position: 70 }]
 meins as MEINS,
 
@UI.lineItem: [{position : 80 , label: 'Amount',  importance: #HIGH }]  
@Semantics.amount.currencyCode: 'WAERK' 
 netwr as NETWR,
 
@UI.lineItem: [{position : 90 , label: 'Currency',  importance: #HIGH }]   
@Semantics.currencyCode: true 
 waerk as WAERK,
 
@ObjectModel.association:{ type: [ #TO_COMPOSITION_PARENT, #TO_COMPOSITION_ROOT ] }
_VBAKCONS
  
}

 

Once above CDS views are defined, create OData service of root CDS consumption view ZC_VBAK_CONS using @OData.publish:true annotation. After that OData service (ZC_VBAK_CONS_CDS) has to be registered using /n/IWFND/MAINT_SERVICE transaction.

Make sure service is in an activated state in SICF transaction.

 

Now I have created a small workflow containing User Decision step

 

Copied standard user decision task into custom one TS90000023 as I have to configure this in SWFVISU.

Now we have to configure this task in SWFVISU transaction.

Created a new entry in SWFVISU with visualization type as ‘My Inbox Generic Application’

 

Visualization parameters will contain service details. Service will be fetching all sales orders, so we need to pass parameters to restrict it to single sales order. Make sure you define parameters in your task container containing the key. In the below case it’s TD_VBELN.

APPLICATION_PATH
COMPONENT_NAME cross.fnd.fiori.inbox.annotationBasedTaskUI
QUERY_PARAM00 service=/sap/opu/odata/sap/ZC_VBAK_CONS_CDS
QUERY_PARAM01 entity=/ZC_VBAK_CONS(‘{&TD_VBELN&}’)
QUERY_PARAM02 annotations=/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName=’ZC_VBAK_CONS_CDS_VAN’,Version=’0001′)/$value/
SCHEME Sapui5

That’s it. Now trigger the Workflow passing sales order number .

In SBWP it will be displayed as a simple User Decision step

 

But on Fiori, it will be displayed as a UI5 application powered by CDS. Annotations used in CDS views are very general annotations,  used to build different kinds of Fiori applications like list applications, master-detail applications, ALP applications. So if you already have exposure to above annotations, then configuring SWFVISU part is the only thing you need to focus on to integrate your workflows with My Inbox generic UI5 applications.

 

3 Comments
You must be Logged on to comment or reply to a post.