Skip to Content

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.

To report this post you need to login first.

4 Comments

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

  1. 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′)” =/

     

    (0) 
  2. Ragini Upadhyay 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.

    (0) 

Leave a Reply