![](https://community.sap.com/html/assets/img_tile-default.png)
Ever come across a really good Standard Fiori app which you would like to use both for users that are allowed to create new business context, but also for those who should only display them? Sometimes there is a "display" or "List" app available, but not always. In this blog you'll learn how you can create your own "display only" app without having to redo everything SAP standard "did for you".
In this example I'll be using "Manage Sales Order" app (F3893): Manage Sales Orders - Version 2
The first thing we have to do is to figure out the main CDS view of the original standard app. In this example it's very simple because with have an OData service that was developed with RAP according to ABAP Cloud: C_SALESORDERMANAGE_SRV. We open this object in ADT and soon find the main CDS: C_SalesOrderManage.
I won't go into details here how to find the main CDS. In another blog post I have explained how to check an OData service and find CDS, please check there. If you still need help, maybe I make a new blog for that topic alone, we'll see.
Now that we have C_SalesOrderManage open (this is a projection view), we can check from which CDS it gets the data; R_SalesOrderTP. Looking at that CDS tells us that this is a root view entity which has composition children. So we can't just select from R_SalesOrderTP in our own CDS and reuse the compositions. For example R_SalesOrderItemTP is linked to R_SalesOrderTP and we cannot change SAP Standard so that it links to our own CDS. Meaning we go 1 level lower to the basic interface view that is used by R_SalesOrderTP; I_SalesOrder. Here some help how the CDS looks like:
We can see that for "as select from" we have I_SalesOrder. This is the one we want to use, because looking at it confirms it's no root view entity, but a regular CDS view/entity which we can reuse for our purpose.
At this point we should make a choice:
Important: both are possible.
@Metadata.ignorePropagatedAnnotations: true
@Metadata.allowExtensions: true
association [0..*] to I_SalesOrderItem as _Item
on $projection.SalesOrder = _Item.SalesOrder
association [0..*] to R_SalesOrderTextTP as _Text
on $projection.SalesOrder = _Text.SalesOrder
association [0..*] to I_SalesOrderPartner as _Partner
on $projection.SalesOrder = _Partner.SalesOrder
association [0..1] to I_SalesOrderPartner as _SoldToParty
on $projection.SalesOrder = _SoldToParty.SalesOrder
and _SoldToParty.PartnerFunction = 'AG'
association [0..1] to C_SlsDocStdPartnerContactInfo as _SoldToPartyContactInfo
on $projection.SalesOrder = _SoldToPartyContactInfo.SalesDocument
and $projection.SoldToParty = _SoldToPartyContactInfo.SoldToParty
and $projection.SalesOrderType = _SoldToPartyContactInfo.SalesDocumentType
association [0..1] to I_SalesOrderPartner as _ShipToParty
on $projection.SalesOrder = _ShipToParty.SalesOrder
and _ShipToParty.PartnerFunction = 'WE'
association [0..1] to I_SlsOrganizationDistrChnl as _SlsOrganizationDistrChnl
on $projection.SalesOrganization = _SlsOrganizationDistrChnl.SalesOrganization
and $projection.DistributionChannel = _SlsOrganizationDistrChnl.DistributionChannel
//Extension Association
association [1] to E_SalesDocumentBasic as _Extension on $projection.SalesOrder = _Extension.SalesDocument
@Search: {
defaultSearchElement: true,
fuzzinessThreshold: 0.9,
ranking: #HIGH }
key SalesOrder.SalesOrder,
@ObjectModel.text.element: ['CustomerName']
@Search: {
defaultSearchElement: true,
fuzzinessThreshold: 0.8 }
SalesOrder.SoldToParty,
@Semantics.text:true
_SoldToParty.FullName as CustomerName,
If you go the more functionality route, you keep creating your z-version for each "composition [0..*] of " that you feel you want to use in your display-only app. Once the wrappers are done, you can come back to the main one and activate it with the correct children-links.
If you paid attention above, we of course want to make sure that users still only see the sales orders they are supposed to see; the standard authorization. Once again, we don't need to redo this, we can inherit the standard one.
In ADT, create a new Access Control with the same name as your CDS you want to be checked, in my case ZC_SalesOrderTP. Then simply enter the "inheriting conditions from entity", save and done:
@EndUserText.label: 'Sales Order for Display Access'
@MappingRole: true
define role ZC_SALESORDERTP {
grant
select on ZC_SalesOrderTP
where
inheriting conditions from entity R_SalesOrderTP;
}
I recommend to add all UI annotations that you want Fiori display via a metadata extension file. In ADT, create a metadata extension file that has the exact same name as your CDS, in my case ZC_SalesOrderTP. Here once again I opened the standard metadata extension file with Ctrl + Shift + A in ADT, and entering the same name as the main CDS: C_SalesOrderManage. In the dialog that shows all the hits, choose the metadata extension file instead of the Data Definition.
Now, at this point you may need some extra time. Instead of blindly copying all the annotations from the standard file, I went through and only added the facets that I wanted on my display-only app, and only the fields that I wanted. If you add too much (meaning you add annotations to something that doesn't exist in your z-wrapper CDS) syntax check will catch it but, there can be some "hidden" errors. And those will result in your Fiori app not functioning properly. For example, let's say you add a certain facet:
{
id: 'CompletionStatus',
label: 'Completion Status',
purpose: #STANDARD,
type: #FIELDGROUP_REFERENCE,
parentId: 'StatusTab',
importance: #HIGH,
position: 30,
targetQualifier: 'CompletionStatus'
},
The above looks all fine and will pass syntax check. We have the "CompletionStatus" and we can add fields to this facet. BUT, if you paid close attention, you may noticed this line: parentId: 'StatusTab'. This means our CompletionStatus facet is inside another, a parent facet. If you now didn't copy that parent facet, and there is no facet with id StatusTab, then your Fiori application will not work properly, because it cannot resolve this.
This is probably the part where you spend most of your time on. Myself when I did this example it took me about 1-2 hours.
Next we need to publish our custom OData service with transaction /IWFND/MAINT_SERVICE for OData V2 services or /IWFND/V4_ADMIN for OData V4. Note that when using @OData.publish: true the system will add _CDS to your OData service name. Confirm your service and metadata was loaded successfully before continuing.
Next up, we create our display-only Fiori elements app.
Open Business Application Studio (BAS) on your SAP BTP and start a space for Fiori development.
Create a new Project from Template and choose SAP Fiori application. The first thing we need to do is connect to a data source. In my case an S/4HANA on-premise system that I have connected to my SAP BTP via Cloud Connector. I select in this step the Destination to that S/4HANA system via Cloud Connector, and then I select my OData service:
Next, we enter the project details, for example:
You should add deployment configuration and also FLP configuration here. I again select my S/4HANA system destination and enter a technical name for the Fiori app to be created on the ABAP platform of my S/4HANA system. You also need to enter package and transport at this point.
For the FLP configuration it's business as usual, except be careful with your semantic object and action. Be sure to not reuse what standard is using, for example I used SalesOrder as object (because that is correct) and then I said the action is "displayOnly" to be sure to not have another intent like that:
Once the wizard is done, simply test your app. Because we have done all the UI annotations, you should be good to go.
Deploy your app and we are almost done.
The app is now in our system but it has no catalog etc. So we gotta do this per usual practice. I won't go into super details here:
All done! You have a display-only app, using standard CDS which you have wrapped into a custom OData service, for a custom app.
And if you were wondering; Is this Clean Core? Then yes. Custom development that follows the ABAP Cloud programming model is "clean". One thing to consider when you create such "wrappers" (Tier 2) is to keep an eye out if SAP Standard delivers a solution that makes your custom development unnecessary; then you should switch to SAP Standard and retire your own development (and you will be on Tier 1).
The final app - list report page (including smart links to customer, without us having to code it ourselves):
Details/Object page:
Hope this was helpful! Let me know if you wonder anything.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
33 | |
13 | |
11 | |
11 | |
10 | |
9 | |
9 | |
9 | |
8 | |
7 |