Skip to Content
Author's profile photo Ragini Upadhyay

FIORI My Inbox 2.0 – Extend Approve Purchase Order – S/4 HANA 1610

After publishing blogs on Custom QM FIORI ECC apps moving ahead with a new experience – S/4 HANA. It might be a cakewalk for HANA experts but as a fresher i struggled and would like to help other beginners to have a smooth ride.

The intent of this blog is to share my experience and solution achieved during the task of extending standard My Inbox 2.0 – Approve Purchase Order App.

Problem Statement:

Add custom fields on S3 view i.e. list of approvers as configured for a release strategy in SPRO.

Displaying the Approver Name along with the Approval Status (Pending / Approved). It seems to be a straightforward approach for ECC apps / Inbox 1.0 but becomes a bit tricky for Inbox 2.0.

Coming to the app, let’s first understand the flow of the app and how it works. Definitely you would get lot of blogs explaining the same.

But a short and sweet TECHNICAL explanation for the techies J :

  1. As compared to ECC fiori apps (PO approval, Shopping Cart approval), My Inbox is a generic inbox for all these functionalities. Again My Inbox 1.0 extension is well explained in Cookbook provided by SAP. My Inbox 2.0 moves one step ahead and the UI is fully dynamic based on annotations.
  1. The configuration is done in SWFVISU transaction. This tells you that the standard workflow task TS20000166 is used and the OData service for PO details used is C_PURCHASEORDER_FS_SRV. The UI is annotation based and uses the annotation file C_PURCHASEORDER_FS_ANNO_MDL. In a simple language, annotation file is nothing but it tells the UI which field to display and where (header, line item etc.).

 

 

3. My Inbox calls the standard OData service /IWPGW/TASKPROCESSING that reads all the workflow items from SWBP for the task configured in SWFVISU. The data from this query can be seen on the left hand side of the app.

/sap/opu/odata/IWPGW/TASKPROCESSING;v=2/TaskCollection?$skip=0&$top=100&$orderby=CreatedOn desc&$inlinecount=allpages&$filter=((TaskDefinitionID eq ‘TS20000166’) and (Status eq ‘READY’ or Status eq ‘RESERVED’ or Status eq ‘IN_PROGRESS’ or Status eq ‘EXECUTED’))&$format=json

 

On click of record on left pane, another set of services are called to get details on the right hand side.

 

GET TaskCollection(SAP__Origin=’LOCAL_PGW’,InstanceID=’000000005007′) -> Task title and other task related details. Here the Workitem Id is passed as input

 

GET C_PurchaseOrderFs(PurchaseOrder=’PO number’) – This service gets the Purchase order details displayed on info tab of S3 view

 

Other details Items/contracts etc are displayed with the below queries:

GET C_PurchaseOrderFs(‘PONUMBER’)/to_PurchaseOrderItem?$skip=0&$top=50&$inlinecount=allpages HTTP/1.1

GET C_PurchaseOrderFs(‘PONUMBER’)/to_PurReqItemByPurOrder?$skip=0&$top=50&$inlinecount=allpages HTTP/1.1

GET C_PurchaseOrderFs(‘PONUMBER’)/to_PurOrdRefPurConItm?$skip=0&$top=50&$inlinecount=allpages HTTP/1.1

GET C_PurchaseOrderFs(‘PONUMBER’)/to_SuplInvPurOrdRef?$skip=0&$top=50&$inlinecount=allpages HTTP/1.1

4. Now fields and services to be displayed are decided from annotation file. This is the most important part of the app. Now this can be seen with below query:

/sap/opu/odata/IWFND/catalogservice;v=2/Annotations(TechnicalName=’C_PURCHASEORDER_FS_ANNO_MDL’,Version=’0001′)/$value

 

<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
	<edmx:Reference
		Uri="../../catalogservice;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_COMMON',Version='0001',SAP__Origin='xxx')/$value">
		<edmx:Include Namespace="com.sap.vocabularies.Common.v1" Alias="Common"/>
	</edmx:Reference>
	<edmx:Reference
		Uri="../../catalogservice;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_UI',Version='0001',SAP__Origin='xxx')/$value">
		<edmx:Include Namespace="com.sap.vocabularies.UI.v1" Alias="UI"/>
	</edmx:Reference>
	<edmx:Reference
		Uri="../../catalogservice;v=2/Vocabularies(TechnicalName='%2FIWBEP%2FVOC_COMMUNICATION',Version='0001',SAP__Origin='xxx')/$value">
		<edmx:Include Namespace="com.sap.vocabularies.Communication.v1" Alias="Communication"/>
	</edmx:Reference>
	<edmx:Reference Uri="../../../sap/c_purchaseorder_fs_srv/$metadata">
		<edmx:Include Namespace="C_PURCHASEORDER_FS_SRV" Alias="SAP"/>
	</edmx:Reference>
	<edmx:Reference Uri="http://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/cs02/vocabularies/Org.OData.Aggregation.V1.xml">
		<edmx:Include Alias="Aggregation" Namespace="Org.OData.Aggregation.V1"/>
	</edmx:Reference>
	<edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/vocabularies/Org.OData.Capabilities.V1.xml">
		<edmx:Include Alias="Capabilities" Namespace="Org.OData.Capabilities.V1"/>
	</edmx:Reference>
	<edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/vocabularies/Org.OData.Core.V1.xml">
		<edmx:Include Alias="Core" Namespace="Org.OData.Core.V1"/>
	</edmx:Reference>
	<edmx:Reference Uri="http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/vocabularies/Org.OData.Measures.V1.xml">
		<edmx:Include Alias="CQP" Namespace="Org.OData.Measures.V1"/>
	</edmx:Reference>
	<edmx:DataServices>
		<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="c_purchaseorder_fs_anno_mdl.v1">
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_PurOrdItemEnhType/Material">
				<Annotation Term="Common.SemanticObject" String="Material"/>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_PurOrdItemEnhType">
				<Annotation Term="UI.LineItem">
					<Collection/>
				</Annotation>
				<Annotation Term="UI.LineItem" Qualifier="PurchItem">
					<Collection>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurchaseOrderItemText"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="MaterialGroup"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="Material"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="FirstDeliveryDate"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="OrderQuantity"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="NetPriceAmount"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurchaseOrderNetPriceQuantity"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="NetAmount"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
					</Collection>
				</Annotation>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_PurOrdRefPurConItmType/PurchaseContract">
				<Annotation Term="Common.SemanticObject" String="PurchaseContract"/>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_PurOrdRefPurConItmType/PurchaseContractItem">
				<Annotation Term="Common.SemanticObject" String="PurchaseContractItem"/>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_PurOrdRefPurConItmType">
				<Annotation Term="UI.FieldGroup" Qualifier="AdditionalData">
					<Record>
						<PropertyValue Property="Data">
							<Collection>
								<Record Type="UI.DataField">
									<PropertyValue Property="Value" Path="Plant"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
								<Record Type="UI.DataField">
									<PropertyValue Property="Value" Path="StorageLocation"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
							</Collection>
						</PropertyValue>
						<PropertyValue Property="Label" String="Additional Data"/>
					</Record>
				</Annotation>
				<Annotation Term="UI.Identification">
					<Collection>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="ReleasedQuantity"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="TargetQuantity"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
					</Collection>
				</Annotation>
				<Annotation Term="UI.LineItem">
					<Collection/>
				</Annotation>
				<Annotation Term="UI.LineItem" Qualifier="ContrItem">
					<Collection>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurchaseContractItem"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurchaseContractType"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="ReleasedQuantity"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="TargetQuantity"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
					</Collection>
				</Annotation>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_PurReqItemByPurOrderType/PurchaseRequisitionItemText">
				<Annotation Term="Common.SemanticObject" String="PurchaseRequisitionItem"/>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_PurReqItemByPurOrderType">
				<Annotation Term="UI.LineItem">
					<Collection/>
				</Annotation>
				<Annotation Term="UI.LineItem" Qualifier="PurchReqItemByPurOrder">
					<Collection>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurchaseRequisitionItemText"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="MaterialGroup"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="Material"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="DeliveryDate"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="RequestedQuantity"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurchaseRequisitionPrice"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurReqnPriceQuantity"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurReqnItemTotalAmount"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
					</Collection>
				</Annotation>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_PurchaseOrderFsType/Supplier">
				<Annotation Term="Common.SemanticObject" String="Supplier"/>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_PurchaseOrderFsType">
				<Annotation Term="UI.DataPoint" Qualifier="DataPoint01">
					<Record>
						<PropertyValue Property="Value" Path="DataPoint01"/>
						<PropertyValue Property="Title" String="Net Value"/>
					</Record>
				</Annotation>
				<Annotation Term="UI.DataPoint" Qualifier="DataPoint02">
					<Record>
						<PropertyValue Property="Value" Path="DataPoint02"/>
						<PropertyValue Property="Title" String="Status"/>
					</Record>
				</Annotation>
				<Annotation Term="UI.DataPoint" Qualifier="PurchaseOrderNetAmount">
					<Record>
						<PropertyValue Property="Value" Path="PurchaseOrderNetAmount"/>
						<PropertyValue Property="Title" String="Net Value"/>
					</Record>
				</Annotation>
				<Annotation Term="UI.DataPoint" Qualifier="PurchasingDocumentStatusName">
					<Record>
						<PropertyValue Property="Value" Path="PurchasingDocumentStatusName"/>
						<PropertyValue Property="Title" String="Status"/>
					</Record>
				</Annotation>
				<Annotation Term="UI.FieldGroup" Qualifier="DelivAndPayment">
					<Record>
						<PropertyValue Property="Data">
							<Collection>
								<Record Type="UI.DataField">
									<PropertyValue Property="Value" Path="PaymentTermsDescription"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
								<Record Type="UI.DataField">
									<PropertyValue Property="Value" Path="IncotermsClassification"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
								<Record Type="UI.DataField">
									<PropertyValue Property="Value" Path="IncotermsTransferLocation"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
							</Collection>
						</PropertyValue>
						<PropertyValue Property="Label" String="Delivery and Payment"/>
					</Record>
				</Annotation>
				<Annotation Term="UI.FieldGroup" Qualifier="Recipient">
					<Record>
						<PropertyValue Property="Data">
							<Collection>
								<Record Type="UI.DataField">
									<PropertyValue Property="Value" Path="CompanyCode"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
								<Record Type="UI.DataField">
									<PropertyValue Property="Value" Path="PurchasingGroup"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
								<Record Type="UI.DataField">
									<PropertyValue Property="Value" Path="PurchasingOrganization"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
								<Record Type="UI.DataField">
									<PropertyValue Property="Criticality" Path="CRITICALITY1"/>
									<PropertyValue Property="Value" Path="APPROVER1NAME"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
								<Record Type="UI.DataField">
									<PropertyValue Property="Criticality" Path="CRITICALITY2"/>
									<PropertyValue Property="Value" Path="APPROVER2NAME"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
								<Record Type="UI.DataField">
									<PropertyValue Property="Criticality" Path="CRITICALITY3"/>
									<PropertyValue Property="Value" Path="APPROVER3NAME"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
								<Record Type="UI.DataField">
									<PropertyValue Property="Criticality" Path="CRITICALITY4"/>
									<PropertyValue Property="Value" Path="APPROVER4NAME"/>
									<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
								</Record>
							</Collection>
						</PropertyValue>
						<PropertyValue Property="Label" String="Recipient"/>
					</Record>
				</Annotation>
				<Annotation Term="UI.HeaderInfo">
					<Record>
						<PropertyValue Property="TypeName" String="Purchase Order"/>
						<PropertyValue Property="TypeNamePlural" String="Purchase Order"/>
						<PropertyValue Property="TypeImageUrl" String="/sap/bc/ui5_ui5/sap/mm_purorders1/images/purchaseorder.png"/>
						<PropertyValue Property="Title">
							<Record Type="UI.DataField">
								<PropertyValue Property="Value" Path="PurchaseOrderType_Text"/>
							</Record>
						</PropertyValue>
						<PropertyValue Property="Description">
							<Record Type="UI.DataField">
								<PropertyValue Property="Value" Path="PurchaseOrder"/>
							</Record>
						</PropertyValue>
					</Record>
				</Annotation>
				<Annotation Term="UI.Identification">
					<Collection>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="Supplier"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="CreationDate"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Label" String="Net Value"/>
							<PropertyValue Property="Value" Path="PurchaseOrderNetAmount"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
					</Collection>
				</Annotation>
				<Annotation Term="UI.LineItem">
					<Collection>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurchaseOrder"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="SupplierName"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PurchasingDocumentStatusName"/>
						</Record>
					</Collection>
				</Annotation>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_SuplInvPurOrdRefType/SupplierInvoice">
				<Annotation Term="Common.SemanticObject" String="SupplierInvoice"/>
			</Annotations>
			<Annotations Target="C_PURCHASEORDER_FS_SRV.C_SuplInvPurOrdRefType">
				<Annotation Term="UI.LineItem">
					<Collection/>
				</Annotation>
				<Annotation Term="UI.LineItem" Qualifier="SuplInvPurOrderRefT">
					<Collection>
						<Record Type="UI.DataField">
							<PropertyValue Property="Label" String="Invoice Number"/>
							<PropertyValue Property="Value" Path="SupplierInvoice"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="SupplierInvoiceIDByInvcgParty"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="DocumentDate"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="PostingDate"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Label" String="Status"/>
							<PropertyValue Property="Value" Path="SupplierInvoiceStatusDesc"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
						<Record Type="UI.DataField">
							<PropertyValue Property="Value" Path="InvoiceGrossAmount"/>
							<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
						</Record>
					</Collection>
				</Annotation>
			</Annotations>
		</Schema>
	</edmx:DataServices>
</edmx:Edmx>

A snapshot of few fields to explain. UI.FieldGroup is annotation used to display a block of fields with similar data. This is the place where the last 3 fields on Info tab are displayed below which we need to display the new fields.

 

 

5.The metadata contains the annotations in below format: It tells us that all the above UI.headerinfo , Ui.lineitem , UI.Identification , UI.FieldGroup are wrapped in UI.Facets . The highlighted part is the FieldGroup part which contains the above fields. The new fields to be added are added in the same Fieldgroup Recipient. This approach was taken as I was not able to add a tag in UI.Facets with a new FieldGroup Identifier. This is something that I am trying to explore. Readers who have solution for this please share it either in comments or a new blog to help others.

 

Trial and Error:

After understanding the problem statement and the behavior of app, the next step was to design the solution .It took some time for me to understand all backend and frontend concepts and to then come up with solution. In this phase I tried many things .

  1. Extended the standard OData service /IWPGW/TASKPROCESSING to include a custom field and was able to display new field in S2 list view but in spite of trying all extension points in S3 I was not able to display the custom field on S3 view as I was unaware of the fact that S3 is annotation based.
  2. Extended the Purchase order OData service and added new fields, tried extending the Annotations through MPC class code by redefining the DEFINE method. But again it dint work.
  3. Tried with local annotation file again that failed. I am not sure if you can use local annotation files for standard Apps.

 

The standard OData service C_PURCHASEORDER_FS_SRV is created through data source references i.e. by using the CDS view, so the CDS view used over here is C_PurchaseOrderFs

 

Runtime Artifacts

 

Final Solution:

 

So finally after trying and testing everything, I discovered that it can be done by extending view and including the inline annotations. Now the approach to include inline annotations was used as the standard CDS view C_PurchaseOrderFs does not allow metadata extension so was not able to extend by using annotate view.

 

In inline declarations I tried creating a new FieldGroup with new identifier but it did not work as I was not successful in including this in metadata with Facets tag.

 

So the final solution:

  1. Custom CDS to get Approvers list ( join using different Standard tables EKKO , T16FW, T16FD , CDPOS , CDHDR , USR21 , ADRP)

 

  1. Custom CDS to arrange those multiple approvers ( row data) to different columns ( in the same row) with Approver Name and Approval Status. I did this as the query called for the info tab data is GET C_PurchaseOrderFs(PurchaseOrder=’PO number’) and accepts only one record.

Two approaches over here again:

 

  1. Group by ebeln and concatenate all approvers in a column separated by comma using table function & AMDP and then split the concatenated row in a new CDS.

Table function:

@EndUserText.label: 'Transpose rows into Columns'
define table function /CTSH/DDSL_TRANSPOSE
returns {
  MANDT: syst_mandt;
  Ebeln : ebeln;
  Approver : /ctsh/approver1;
  VALUE_NEW :/ctsh/approver1;
  Username : char12;  
  
}
implemented by method /CTSH/TRANSPOSE=>transpose;

 

AMDP Class and Method

CLASS /ctsh/transpose DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
  INTERFACES if_amdp_marker_hdb.

CLASS-METHODS transpose FOR TABLE FUNCTION /CTSH/DDSL_TRANSPOSE.

  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS /ctsh/transpose IMPLEMENTATION.

method transpose by database function
                   for hdb
                   language sqlscript
                   options read-only
                   using /CTSH/DDLS_POREL.

  return select mandt,
               ebeln,
                STRING_AGG(objid, ',') AS APPROVER,
                from "/CTSH/DDLS_POREL"
                      group by MANDT ,ebeln ;
endmethod.

ENDCLASS.

 

CDS view using table function

@AbapCatalog.sqlViewName: '/CTSH/DDSL_APPR'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Final approvers Table'
define view /CTSH/ddsl_final as select from /CTSH/DDSL_TRANSPOSE

{
    Ebeln,
    Approver,
    substring(Approver,1,6) as APPROVER1,
    substring(Approver,8,6) as APPROVER2,
    substring(Approver,15,6) as APPROVER3,
    substring(Approver,22,6) as APPROVER4,
    substring(Approver,29,6) as APPROVER5,
    substring(Approver,36,6) as APPROVER6
    
} 

 

b) Join on the same table

@AbapCatalog.sqlViewName: '/CTSH/DDSL_ANAME'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Approvers Name'

define view /CTSH/ddsl_name as select distinct from /ctsh/cdsv_PoRel
 
 left outer join /ctsh/cdsv_PoRel as aa1 on  aa1.ebeln = /ctsh/cdsv_PoRel.ebeln
                                  and aa1.frgco = '01'
 left outer join /ctsh/cdsv_PoRel as bb1 on  bb1.ebeln = /ctsh/cdsv_PoRel.ebeln
                                  and bb1.frgco = '02' 
 left outer join /ctsh/cdsv_PoRel as cc1 on  cc1.ebeln = /ctsh/cdsv_PoRel.ebeln
                                   and cc1.frgco = '03' 
 left outer join /ctsh/cdsv_PoRel as dd1 on  dd1.ebeln = /ctsh/cdsv_PoRel.ebeln
                                   and dd1.frgco = '04' 
 left outer join /ctsh/cdsv_PoRel as ee1 on  ee1.ebeln = /ctsh/cdsv_PoRel.ebeln
                                    and ee1.frgco = '05'                                                                                                                                

{
    /ctsh/cdsv_PoRel.ebeln,
    
    aa1.value_new as App1Stat,
    aa1.objid as Approver1,
        case 
        when aa1.value_new <> '' 
            then CONCAT(aa1.Name , ' - Approved') 
            else CONCAT(aa1.ApproverName , ' - Pending')
        end as APPROVER1Name, 
    
      case 
      when aa1.value_new <> ''
        then cast(3 as abap.int4)        
        else cast(2 as abap.int4)
      end as Criticality1,       
    
    bb1.value_new as App2Stat,
    bb1.objid as Approver2,
        case 
        when bb1.value_new <> '' 
            then CONCAT(bb1.Name, ' - Approved')  
            else CONCAT(bb1.ApproverName , ' - Pending')
        end as APPROVER2Name,
       
      case  
      when bb1.value_new <> ''
        then cast(3 as abap.int4)        
        else cast(2 as abap.int4)
      end as Criticality2,   
    
    cc1.value_new as App3Stat,
    cc1.objid as Approver3,
        case 
        when cc1.value_new <> '' 
            then CONCAT(cc1.Name, ' - Approved')  
            else CONCAT(cc1.ApproverName, ' - Pending')
        end as APPROVER3Name, 
    
      case  
      when cc1.value_new <> ''
        then cast(3 as abap.int4)        
        else cast(2 as abap.int4)
      end as Criticality3,   
        
    dd1.value_new as App4Stat,
    dd1.objid as Approver4,
        case 
        when dd1.value_new <> '' 
            then CONCAT(dd1.Name, ' - Approved')  
            else CONCAT(dd1.ApproverName, ' - Pending')
        end as APPROVER4Name,    
    
      case  
      when dd1.value_new <> ''
        then cast(3 as abap.int4)        
        else cast(2 as abap.int4)
      end as Criticality4,       
    
    ee1.value_new as App5Stat,
    ee1.objid as Approver5,
        case 
        when ee1.value_new <> '' 
            then CONCAT(ee1.Name , ' - Approved')  
            else CONCAT(ee1.ApproverName , ' - Pending')
        end as APPROVER5Name,
        
      case  
      when ee1.value_new <> ''
        then cast(3 as abap.int4)        
        else cast(2 as abap.int4)
      end as Criticality5   
         
} 

3. Extend Standard CDS with inline annotations

 

@AbapCatalog.sqlViewAppendName: '/CTSH/DDSL_EXT'
@EndUserText.label: 'Approve PO Extension'

extend view C_PurchaseOrderFs with /CTSH/CDSV_EXT 
association [1..1] to /CTSH/ddsl_name as _Approver 
on I_PurchaseOrderEnhanced.purchaseorder = _Approver.ebeln
{
      @UI.fieldGroup: [{
           qualifier: 'Recipient',
           groupLabel: 'Recipient',           
           criticality: 'CRITICALITY1', 
           position: 40,
           exclude: false,
           importance: #HIGH,
           value: 'APPROVER1NAME' }]  
        @EndUserText.label: 'Approver Level 1' 
       _Approver.APPROVER1Name,
       _Approver.Criticality1,
       
     
      @UI.fieldGroup: [{
           qualifier: 'Recipient',
           groupLabel: 'Recipient',
           criticality: 'CRITICALITY2', 
           position: 50,
           exclude: false,
           importance: #HIGH,
           value: 'APPROVER2NAME' }]
       @EndUserText.label: 'Approver Level 2'           
       _Approver.APPROVER2Name,
       _Approver.Criticality2,
       
      @UI.fieldGroup: [{
           qualifier: 'Recipient',
           groupLabel: 'Recipient',
           criticality: 'CRITICALITY3', 
           position: 60,
           exclude: false,
           importance: #HIGH,
           value: 'APPROVER3NAME' }] 
        @EndUserText.label: 'Approver Level 3'             
       _Approver.APPROVER3Name,
       _Approver.Criticality3,
       
      @UI.fieldGroup: [{
           qualifier: 'Recipient',
           groupLabel: 'Recipient',
           criticality: 'CRITICALITY4',  
           position: 70,
           exclude: false,
           importance: #HIGH,
           value: 'APPROVER4NAME' }]
      @EndUserText.label: 'Approver Level 4'
       _Approver.APPROVER4Name ,
       _Approver.Criticality4            
} 

 

This extends the standard View as below:

Extends Annotations as below: Added Criticality for adding yellow if pending , else green if approved .

 

As the Recipient Field Group is already a part of standard Metadata file, the new fields gets added to the annotated file and fields gets displayed on UI

Final Output:

 

After reading the blog, readers might come up with many questions and alternative approach for the same problem statement. Please feel free to post your comments, suggestions and answers for the Facets tag.

Assigned Tags

      25 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Naveen Kumar
      Naveen Kumar

      Excellent !!

       

      Author's profile photo Matheus Oliveira Goulart
      Matheus Oliveira Goulart

      Thanks for the Blog! Excellent!

       

      I`m analysing how to do this enhacement at a S/4 HANA 1511 enviroment.The annotations model do not appear at "/sap/opu/odata/IWFND/catalogservice;v=2/Annotations(TechnicalName=’C_PURCHASEORDER_FS_ANNO_MDL’,Version=’0001′)" =/

       

      Author's profile photo Ragini Upadhyay
      Ragini Upadhyay
      Blog Post Author

      Hi Matheus,

       

      My Inbox S/4 HANA 1511 is not annotation based. SAP has published a cookbook for extending My Inbox 1.0. Please refer that.

      Author's profile photo Dimitrios Kavallieratos
      Dimitrios Kavallieratos

      Excellent blog Ragini!

      However , is there a way to extend the Icon Tab Bar via annotations ??

      Adding a new button with additional data in it??

       

      Thank you 🙂

       

       

       

       

      Author's profile photo Ragini Upadhyay
      Ragini Upadhyay
      Blog Post Author

      Thanks Dimitrios!!!

      I need to check this!!

      Author's profile photo ramesh gandham
      ramesh gandham

      Hi Ragini,

       

      Thanks for explaining in clear way!!!!

      I have a requirement in purchase requisition approval and i have to show total net price of sum of line item net price in header section of S3 view.

      So do i need to calculate net price value in CDS view and pass UI via inline annotation.

      Thanks for your help.

      Thanks & Best Regards,

      Ramesh

      Author's profile photo Fabio Junckes
      Fabio Junckes

      Hello Ramesh,

      Could you please share how did you solve your request? I have the same requirement.

      I noticed that the view C_PurRequisitionItemFs alreayd has the field PurReqnItemTotalAmount.

      But I don't know how to display this information on My Inbox App.

       

      Thanks.

      Best regards.

      Fabio

      Author's profile photo Olivier Souksamran
      Olivier Souksamran

      Hi dear all,

      Thank you for this very detailed blog. It is really interesting.

      My requirement is to hide fields / table columns / detail view blocks... As far as I know, UI.hidden annotation could be used but I can't understand how we can override standard annotations...

      @UI.hidden

      We can't extend standard CDS with already exposed fields...

      So is it possible to redefine, override the annotation file specified in the service :

      /sap/opu/odata/IWFND/catalogservice;v=2/Annotations(TechnicalName=’C_PURCHASEORDER_FS_ANNO_MDL’,Version=’0001′)/$value

      Or is it possible to create another one ?

      Regards,

      Olivier

      Author's profile photo B. van de Kamp
      B. van de Kamp

      Hi Olivier,

      Have you been able to create a local annotation file and overwrite the standard one? If yes, please share 🙂

      Regards,

       

      Bart

      Author's profile photo Olivier Souksamran
      Olivier Souksamran

      Dear Bart,

      Not yet. I am still looking for some solution.

      Regards,

      Olivier

      Author's profile photo B. van de Kamp
      B. van de Kamp

      Hi Olivier,

      We're thinking to try next approach:

      • Just create a copy of the standard CDS C_PURCHASEORDERFS to ZC_PURCHASEORDERFS
      • Change the annotations in ZC_PURCHASEORDERFS
      • Register as a service on the Gateway
      • Configure parameter QUERY_PARAM02 in transaction SWFVISU for task TS00800531 and set value  to annotations=/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='ZC_PURCHASEORDER_FS_ANNO_MDL',Version='0001')/$value/
      • Configure parameter QUERY_PARAM01 in transaction SWFVISU  and set value to service=/sap/opu/odata/sap/ZC_PURCHASEORDER_FS_SRV (Maybe not required as we just want to change the metadata and not the CDS view itself)

      In case of upgrade and impact on C_PURCHASEORDERFS then the ZC_PURCHASEORDERFS must also be checked and changed....

      Grtz,

      Bart

      Author's profile photo Olivier Souksamran
      Olivier Souksamran

      Dear B. van de Kamp ,

      Happy to see update from you.

      Actually, I've already tried the very same approach by creating a copy from the standard CDS C_PURCHASEORDERFS and also by creating another CDS on top of it and invoking each of the fields and associations.

      As far as I can remember, it did not work :(. Maybe I will check again, I may have missed something...

      Have you any feedback on this ?

      Lastly, I am trying to get this working by extending the standard App in WebIDE and adding local annotation file... I get this error : "Echec de l'appel du service avec l'URL suivant : annotation/C_PURCHASEORDER_FS_ANNO_MDL.xml" which basically means that the local annotation file could not be found.

      I begin to lose hope 🙁

      Regards,

      Olivier

       

      Author's profile photo Olivier Souksamran
      Olivier Souksamran

      Dear B. van de Kamp

      I finally found a solution !

      The main steps are to a local annotation file from the standard CDS C_PURCHASEORDERFS via WebIDE, to export it and to publish it on the gateway, and to change the QUERY_PARAM02 in order to refer the published local annotation file.

      Please ring me if you need any deeper explanations.

      Regards,

      Olivier

      Author's profile photo Anupam Shrotriya
      Anupam Shrotriya

      Dear

       

      How did you publish annotation file “C_PURCHASEORDER_FS_ANNO_MDL”.xml on the gateway and then refer same to QUERY_PARAM02 ?

      Please share screen shots if possible.

      Thanks & Regards,

      Anu

      Author's profile photo Anupam Shrotriya
      Anupam Shrotriya

      Dear

       

      It’s urgent. I have download Annotation file “C_PURCHASEORDER_FS_ANNO_MDL”.xml using WebIDE but how to publish on gateway in order  to define path of it to QUERY_PARAM02.

      Thanks & Regards,

      Anu

      Author's profile photo Olivier Souksamran
      Olivier Souksamran

      Dear Anupam Shrotriya

      You just need to publish your XML annotation file as a MIME ressources. Then you can access it from your configuration of QUERY_PARAM02.

      Sorry, it seems I can't insert my screen shot 🙁

      Regards,

      Olivier

      Author's profile photo Anupam Shrotriya
      Anupam Shrotriya

      Dear

      I have uploaded annotation xml file in frontend server in path : /SAP/BC/BSP/SAP/CA_FIORI_INBOX/C_PURCHASEORDER_FS_ANNO_MDL.xml
      Used the same path in backend for QUERY_PARAM02 as below:
      annotations=/SAP/BC/BSP/SAP/CA_FIORI_INBOX/C_PURCHASEORDER_FS_ANNO_MDL.xml
      Now it is working :). So uploading local annotation file in folder ca_fiori_inbox is good? or which exact path you have used to upload local annotation file in frontend server?
      Thanks & Regards,
      Anu
      Author's profile photo Olivier Souksamran
      Olivier Souksamran

      Dear Anupam Shrotriya

      Actually, I'd rather publish it in a custom BSP location since your local annotation is no standard 🙂

      But technically, it does work !

      Cheers !

      Olivier

      Author's profile photo Suwandi Cahyadi
      Suwandi Cahyadi

      Hi,

      May I know how do you manage to download the annotation file?

      Thank you

      Author's profile photo Suwandi Cahyadi
      Suwandi Cahyadi

      Hi,

      May I know how do you manage to download the annotation file?

      Thank you

      Author's profile photo Olivier Souksamran
      Olivier Souksamran

      Hi,

      Do you mean to download it from standard service so you can get the file in local ?

      Just hit the URL from your gateway :

      /sap/opu/odata/IWFND/catalogservice;v=2/Annotations(TechnicalName=’C_PURCHASEORDER_FS_ANNO_MDL’,Version=’0001′)/$value

      It should launch the download of the annotation XML file.

      Regards,

      Olivier

      Author's profile photo ZALAK DALAL
      ZALAK DALAL

      Hi Ragini,

      It is very helpful blog.

      I am facing issue in extending S2 view and related oData service.

      I have created extended odata service by redefinition of taskprocessing service and added one filed Supplier in Tasks Entity set.

      I am filling data in Supplier field by following steps explained in  "Extending SAP Fiori My Inbox" cookbook but not getting values in Supplier field when i run odata service from gateway client.

      When I checked this odata in Debug mode,data is getting filled in Supplier(newly added field) in ER_Entityset but after that it gets empty.

      Is there anything need to be done for Hana which is not mentioned in cookbook?

      I appreciate if you can help me for this issue as I saw you have already extended S2 view.

       

      Regards,

      Zalak

      Author's profile photo Hakan Erdogan
      Hakan Erdogan

      Hi Zalak,

       

      I'm having the same problem. did you solve the problem?

      Author's profile photo VS G
      VS G

      Ragini, I followed the similar approach and was able to see the fields in the Fiori inbox. However, I noticed that "Criticality" is not working in my case. I noticed the same in the screenshots of your blog. Did you happen to get the criticality working?

      Author's profile photo Devin Reid
      Devin Reid

      Hi Ragini,

      Your blog is well written and informative, but I am struggling to understand why the solution is so complex when we have a very straightforward way of accomplishing this via View Extension.  Unless I am missing some of your requirements, please see my solution here:

      1. Create CDS Data Definition.
      2. Select 'Extend View'
      3. Define your extension like below.  I have hardcoded a string value, but all you need to do is create your custom CDS view separately with all your logic to select approvers and other data, then join it to C_PurchaseOrderFs.  Example, adding 1 approver:
        Simple%20CDS%20Extension
      4. Result: