Recently I did extension for My Inbox Fiori application for Purchase Order and Purchase Requisition approvals. As beginner, I did not know where to start initially. After some research I found a blog from Ragini Upadhyay (https://blogs.sap.com/2018/06/02/fiori-my-inbox-2.0-extend-approve-purchase-order-s4-hana-1610/) which was well explained but I had to struggled a lot. There are few places where this solution wasn’t accurate and wasn’t working. This might be because of the S4 version. Then I found some workaround and finally I’m able to achieve it. Let’s hear it…
Requirement:
Display approvers along with status in detail screen for both purchase order and purchase requisition.
Technical Details:
My Inbox detail screen for PR & PO is cds annotation based. In this case, annotations is being loaded from service model and below links are loading these annotations in the app.
Annotation Purchase Order:
/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='C_PURREQUISITION_FS_ANNO_MDL',Version='0001')/$value
Annotation Purchase Requisition:
/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='C_PURCHASEORDER_FS_ANNO_MDL',Version='0001')/$value
Standard CDS View for Extenstion:
PR – C_PurchaseOrderFs
PO – C_PurRequisitionFs
Standard Tables:
PR – EBAN (PR Header Details and Release Strategy) -> T16FS (Release Codes from Release Strategy) -> T16FD (Release Codes from Release Group) -> T16FW (User Id’s from Release Codes) -> USR21 (Personnel Number from User Id) -> ADRP (User Full Name from Personal Number)
PO – EKKO (PO Header Details) -> T16FS (Release Codes from Release Strategy) -> T16FD (Release Codes from Release Group) -> T16FW (User Id’s from Release Codes) -> USR21 (Personnel Number from User Id) -> ADRP (User Full Name from Personal Number)
Purchase Order Approvals (4 Levels)
- Create CDS View ZCDS_PUR_ORD_APP to read Approver ID from T16FW. Joining EKKO on T16FW and T16FD.
@AbapCatalog.sqlViewName: 'ZSQ_PURORD_APP'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'CDS for PO Approvals'
define view ZCDS_PUR_ORD_APP as select from ekko as _PurOrd
left outer join t16fs as _RelStat on _PurOrd.frggr = _RelStat.frggr
and _PurOrd.frgsx = _RelStat.frgsx
left outer join t16fw as _RoleRelCode on _PurOrd.frggr = _RoleRelCode.frggr
left outer join t16fd as _RelCodeDesc on _PurOrd.frggr = _RelCodeDesc.frggr
and _RoleRelCode.frgco = _RelCodeDesc.frgco
{
_PurOrd.ebeln,
_PurOrd.frggr,
_PurOrd.frgsx,
_PurOrd.frgke,
_PurOrd.frgzu,
_RoleRelCode.objid,
_RoleRelCode.otype,
_RelCodeDesc.frgct,
_RelCodeDesc.frgco,
_RelStat.frgc1,
_RelStat.frgc2,
_RelStat.frgc3,
_RelStat.frgc4
}
Result of ZCDS_PUR_ORD_APP (Showing approvers in multiple rows)
Objid = User ID
Frgct = User Position
- Now we need to Create CDS view ZCDS_PUR_ORD_APP_F to convert multiple rows into single row. Also, association added to read user full name from CDS ZCDS_USER_DET.
Example:
Here we go…
@AbapCatalog.sqlViewName: 'ZSQ_PO_APPF'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'PO Approvals Final V2'
define view ZCDS_PUR_ORD_APP_F as select from ZCDS_PUR_ORD_APP
association [1..1] to ZCDS_USER_DET as _UserDet on ZCDS_PUR_ORD_APP.objid = _UserDet.UserId {
ebeln,
max( case frgco when frgc1 then objid end ) as ApproverID1,
max( case frgco when frgc2 then objid end ) as ApproverID2,
max( case frgco when frgc3 then objid end ) as ApproverID3,
max( case frgco when frgc4 then objid end ) as ApproverID4,
max( case frgco when frgc1 then frgct end ) as ApprPosition1,
max( case frgco when frgc2 then frgct end ) as ApprPosition2,
max( case frgco when frgc3 then frgct end ) as ApprPosition3,
max( case frgco when frgc4 then frgct end ) as ApprPosition4,
max( case frgco when frgc1 then _UserDet.UserFName end ) as ApproverName1,
max( case frgco when frgc2 then _UserDet.UserFName end ) as ApproverName2,
max( case frgco when frgc3 then _UserDet.UserFName end ) as ApproverName3,
max( case frgco when frgc4 then _UserDet.UserFName end ) as ApproverName4,
substring(frgzu,1,1) as ApprovedLevel1,
substring(frgzu,2,1) as ApprovedLevel2,
substring(frgzu,3,1) as ApprovedLevel3,
substring(frgzu,4,1) as ApprovedLevel4
} group by ebeln, frgzu
Result
- CDS ZCDS_USER_DET for User Details
@AbapCatalog.sqlViewName: 'ZSQ_USRDET'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'User Details'
define view ZCDS_USER_DET as select from usr21 as _User
inner join adrp as _PersDet
on _User.persnumber = _PersDet.persnumber {
key _User.bname as UserId,
_User.persnumber as PersNo,
_PersDet.name_text as UserFName
}
- Now we need to create extension on Standard CDS C_PurchaseOrderFs association on ZCDS_PUR_ORD_APP_F along with annotations. I have copied annotations from standard cds for one field and made changes for our new fields.
@AbapCatalog.sqlViewAppendName: 'ZSQ_PURORDFS_EXT'
@EndUserText.label: 'Extension for C_PurchaseOrderFs'
extend view C_PurchaseOrderFs with ZC_PurchaseOrderFs_EXT
association [1..1] to ZCDS_PUR_ORD_APP_F as _Approver on I_PurchaseOrderEnhanced.purchaseorder = _Approver.ebeln
{
@UI.fieldGroup: [{
qualifier: 'Recipient',
groupLabel: 'Recipient', groupLabel_asOtr: 'FA163EDF73161EE587C742C45B2B0F3D',
position: 40,
exclude: false,
importance: #HIGH }, {
qualifier: 'Detail3',
groupLabel: 'Recipient', groupLabel_asOtr: 'FA163EDF73161EE587C742C45B2B0F3D',
position: 40,
exclude: false,
importance: #HIGH
}]
@EndUserText.label: 'Approver Level 1'
case when _Approver.ApprovedLevel1 = 'X'
then CONCAT(_Approver.ApproverName1, ' - Approved')
else CONCAT(_Approver.ApproverName1, ' - Pending')
end as ApproverID1,
// _UserDet.UserFName as ApproverName1,
@UI.fieldGroup: [{
qualifier: 'Recipient',
groupLabel: 'Recipient', groupLabel_asOtr: 'FA163EDF73161EE587C742C45B2B0F3D',
position: 50,
exclude: false,
importance: #HIGH }, {
qualifier: 'Detail3',
groupLabel: 'Recipient', groupLabel_asOtr: 'FA163EDF73161EE587C742C45B2B0F3D',
position: 50,
exclude: false,
importance: #HIGH
}]
@EndUserText.label: 'Approver Level 2'
case when _Approver.ApprovedLevel2 = 'X'
then CONCAT(_Approver.ApproverName2, ' - Approved')
else CONCAT(_Approver.ApproverName2, ' - Pending')
end as ApproverID2,
@UI.fieldGroup: [{
qualifier: 'Recipient',
groupLabel: 'Recipient', groupLabel_asOtr: 'FA163EDF73161EE587C742C45B2B0F3D',
position: 60,
exclude: false,
importance: #HIGH }, {
qualifier: 'Detail3',
groupLabel: 'Recipient', groupLabel_asOtr: 'FA163EDF73161EE587C742C45B2B0F3D',
position: 60,
exclude: false,
importance: #HIGH
}]
@EndUserText.label: 'Approver Level 3'
case when _Approver.ApprovedLevel3 = 'X'
then CONCAT(_Approver.ApproverName3, ' - Approved')
else CONCAT(_Approver.ApproverName3, ' - Pending')
end as ApproverID3,
@UI.fieldGroup: [{
qualifier: 'Recipient',
groupLabel: 'Recipient', groupLabel_asOtr: 'FA163EDF73161EE587C742C45B2B0F3D',
position: 70,
exclude: false,
importance: #HIGH }, {
qualifier: 'Detail3',
groupLabel: 'Recipient', groupLabel_asOtr: 'FA163EDF73161EE587C742C45B2B0F3D',
position: 70,
exclude: false,
importance: #HIGH
}]
@EndUserText.label: 'Approver Level 4'
case when _Approver.ApprovedLevel4 = 'X'
then CONCAT(_Approver.ApproverName4, ' - Approved')
else CONCAT(_Approver.ApproverName4, ' - Pending')
end as ApproverID4
}
That’s it. We are done.
Purchase Requisition Approvals (8 Levels):
- Create CDS ZCDS_PUR_REQ_APP to read Approver ID from T16FW. Joining EBAN on T16FS, T16FW and T16FD. Here we are taking one more table T16FS additionally, from this we are taking release strategies and related release codes. As there are many release strategies are defined based on multiple conditions.
@AbapCatalog.sqlViewName: 'ZSQ_PURREQ_APP'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'CDS for PR Approvals'
define view ZCDS_PUR_REQ_APP as select from eban as _PurReq
left outer join t16fs as _RelStat on _PurReq.frggr = _RelStat.frggr
and _PurReq.frgst = _RelStat.frgsx
left outer join t16fd as _RelCodeDesc on _PurReq.frggr = _RelCodeDesc.frggr
and ( _RelStat.frgc1 = _RelCodeDesc.frgco
or _RelStat.frgc2 = _RelCodeDesc.frgco
or _RelStat.frgc3 = _RelCodeDesc.frgco
or _RelStat.frgc4 = _RelCodeDesc.frgco
or _RelStat.frgc5 = _RelCodeDesc.frgco
or _RelStat.frgc6 = _RelCodeDesc.frgco
or _RelStat.frgc7 = _RelCodeDesc.frgco
or _RelStat.frgc8 = _RelCodeDesc.frgco )
left outer join t16fw as _RoleRelCode on _PurReq.frggr = _RoleRelCode.frggr
and _RelCodeDesc.frgco = _RoleRelCode.frgco
{
_PurReq.banfn,
_PurReq.frgkz,
_PurReq.frgzu,
_PurReq.frgst,
_PurReq.frggr,
_RelStat.frgc1,
_RelStat.frgc2,
_RelStat.frgc3,
_RelStat.frgc4,
_RelStat.frgc5,
_RelStat.frgc6,
_RelStat.frgc7,
_RelStat.frgc8,
_RoleRelCode.objid,
_RoleRelCode.otype,
_RelCodeDesc.frgct,
_RelCodeDesc.frgco
}
- Now we need to Create CDS view ZCDS_PUR_REQ_APP_F to convert multiple rows into single row. Also, association added to read user full name from CDS ZCDS_USER_DET.
@AbapCatalog.sqlViewName: 'ZSQ_PR_APPF'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'PR Approvals Final V2'
define view ZCDS_PUR_REQ_APP_F as select from ZCDS_PUR_REQ_APP
association [1..1] to ZCDS_USER_DET as _UserDet on ZCDS_PUR_REQ_APP.objid = _UserDet.UserId {
banfn,
max( case frgco when frgc1 then objid end ) as ApproverID1,
max( case frgco when frgc2 then objid end ) as ApproverID2,
max( case frgco when frgc3 then objid end ) as ApproverID3,
max( case frgco when frgc4 then objid end ) as ApproverID4,
max( case frgco when frgc5 then objid end ) as ApproverID5,
max( case frgco when frgc6 then objid end ) as ApproverID6,
max( case frgco when frgc7 then objid end ) as ApproverID7,
max( case frgco when frgc8 then objid end ) as ApproverID8,
max( case frgco when frgc1 then _UserDet.UserFName end ) as ApproverName1,
max( case frgco when frgc2 then _UserDet.UserFName end ) as ApproverName2,
max( case frgco when frgc3 then _UserDet.UserFName end ) as ApproverName3,
max( case frgco when frgc4 then _UserDet.UserFName end ) as ApproverName4,
max( case frgco when frgc5 then _UserDet.UserFName end ) as ApproverName5,
max( case frgco when frgc6 then _UserDet.UserFName end ) as ApproverName6,
max( case frgco when frgc7 then _UserDet.UserFName end ) as ApproverName7,
max( case frgco when frgc8 then _UserDet.UserFName end ) as ApproverName8,
substring(frgzu,1,1) as ApprovedLevel1,
substring(frgzu,2,1) as ApprovedLevel2,
substring(frgzu,3,1) as ApprovedLevel3,
substring(frgzu,4,1) as ApprovedLevel4,
substring(frgzu,5,1) as ApprovedLevel5,
substring(frgzu,6,1) as ApprovedLevel6,
substring(frgzu,7,1) as ApprovedLevel7,
substring(frgzu,8,1) as ApprovedLevel8
} group by banfn, frgzu
- Now we need to create extension on Standard CDS C_PurRequisitionFs association on ZCDS_PUR_REQ_APP_F along with annotations. I have copied annotations from standard cds for one field and made changes for our new fields.
@AbapCatalog.sqlViewAppendName: 'ZSQ_PURREQFS_EXT'
@EndUserText.label: 'Extension for C_PurRequisitionFs'
extend view C_PurRequisitionFs with ZC_PurRequisitionFs_EXT
association [1..1] to ZCDS_PUR_REQ_APP_F as _Approver on I_Purchaserequisition.purchaserequisition = _Approver.banfn {
@EndUserText: {label: 'Approver Level 1'}
@UI.identification:{label: 'Approver Level 1', importance: #HIGH, position: 500}
@UI.lineItem:{label: 'Approver Level 1', importance: #MEDIUM, position: 500}
case when _Approver.ApprovedLevel1 = 'X'
then CONCAT(_Approver.ApproverName1, ' - Approved')
else CONCAT(_Approver.ApproverName1, ' - Pending')
end as ApproverID1,
@EndUserText: {label: 'Approver Level 2'}
@UI.identification:{label: 'Approver Level 2', importance: #HIGH, position: 600}
@UI.lineItem:{label: 'Approver Level 2', importance: #MEDIUM, position: 600}
case when _Approver.ApprovedLevel2 = 'X'
then CONCAT(_Approver.ApproverName2, ' - Approved')
else CONCAT(_Approver.ApproverName2, ' - Pending')
end as ApproverID2,
@EndUserText: {label: 'Approver Level 3'}
@UI.identification:{label: 'Approver Level 3', importance: #HIGH, position: 700}
@UI.lineItem:{label: 'Approver Level 3', importance: #MEDIUM, position: 700}
case when _Approver.ApprovedLevel3 = 'X'
then CONCAT(_Approver.ApproverName3, ' - Approved')
else CONCAT(_Approver.ApproverName3, ' - Pending')
end as ApproverID3,
@EndUserText: {label: 'Approver Level 4'}
@UI.identification:{label: 'Approver Level 4', importance: #HIGH, position: 800}
@UI.lineItem:{label: 'Approver Level 4', importance: #MEDIUM, position: 800}
case when _Approver.ApprovedLevel4 = 'X'
then CONCAT(_Approver.ApproverName4, ' - Approved')
else CONCAT(_Approver.ApproverName4, ' - Pending')
end as ApproverID4,
@EndUserText: {label: 'Approver Level 5'}
@UI.identification:{label: 'Approver Level 5', importance: #HIGH, position: 900}
@UI.lineItem:{label: 'Approver Level 5', importance: #MEDIUM, position: 900}
case when _Approver.ApprovedLevel5 = 'X'
then CONCAT(_Approver.ApproverName5, ' - Approved')
else CONCAT(_Approver.ApproverName5, ' - Pending')
end as ApproverID5,
@EndUserText: {label: 'Approver Level 6'}
@UI.identification:{label: 'Approver Level 6', importance: #HIGH, position: 1000}
@UI.lineItem:{label: 'Approver Level 6', importance: #MEDIUM, position: 1000}
case when _Approver.ApprovedLevel6 = 'X'
then CONCAT(_Approver.ApproverName6, ' - Approved')
else CONCAT(_Approver.ApproverName6, ' - Pending')
end as ApproverID6,
@EndUserText: {label: 'Approver Level 7'}
@UI.identification:{label: 'Approver Level 7', importance: #HIGH, position: 1100}
@UI.lineItem:{label: 'Approver Level 7', importance: #MEDIUM, position: 1100}
case when _Approver.ApprovedLevel7 = 'X'
then CONCAT(_Approver.ApproverName7, ' - Approved')
else CONCAT(_Approver.ApproverName7, ' - Pending')
end as ApproverID7,
@EndUserText: {label: 'Approver Level 8'}
@UI.identification:{label: 'Approver Level 8', importance: #HIGH, position: 1200}
@UI.lineItem:{label: 'Approver Level 8', importance: #MEDIUM, position: 1200}
case when _Approver.ApprovedLevel8 = 'X'
then CONCAT(_Approver.ApproverName8, ' - Approved')
else CONCAT(_Approver.ApproverName8, ' - Pending')
end as ApproverID8
}
Hi! Thank you very much! I think some screenshots are missing. Can you please check? Thanks!