https://raw.githubusercontent.com/ajmaradiaga/feeds/main/scmt/topics/ABAP-RESTful-Application-Programming-Model-blog-posts.xml SAP Community - ABAP RESTful Application Programming Model 2024-07-26T23:00:06.808158+00:00 python-feedgen ABAP RESTful Application Programming Model blog posts in SAP Community https://community.sap.com/t5/technology-blogs-by-sap/how-to-search-in-the-new-sap-community-for-the-latest-content-related-to-an/ba-p/13637243 How to search in the new SAP community for the latest content related to an SAP managed tag? 2024-03-13T17:46:57.400000+01:00 Andre_Fischer https://community.sap.com/t5/user/viewprofilepage/user-id/55 <P>The workaround is very simple. I am basically searching for the SAP managed tag as a string by surrounding the tag in question by double quotes, for example:<BR />"ABAP RESTful Application Programming Model".</P><P>In addition I choose a sortorder by Date so that the newest posts will show up first.</P><P>So the following link will produce a list of the latest posts that have used the SAP managed tag&nbsp;"ABAP RESTful Application Programming Model".</P><P>It is also possible to store such a search as an RSS Feed.</P><P data-unlink="true"><A href="https://community.sap.com/t5/forums/searchpage/tab/message?q=%22ABAP%20RESTful%20Application%20Programming%20Model%22&amp;noSynonym=false&amp;sort_by=-topicPostDate&amp;collapse_discussion=true&nbsp;" target="_blank">https://community.sap.com/t5/forums/searchpage/tab/message?q=%22ABAP%20RESTful%20Application%20Programming%20Model%22&amp;noSynonym=false&amp;sort_by=-topicPostDate&amp;collapse_discussion=true&nbsp;</A></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="search for sap managed tag.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/80144i8F2528E6A4776277/image-size/large?v=v2&amp;px=999" role="button" title="search for sap managed tag.png" alt="search for sap managed tag.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RSS Feed.png" style="width: 637px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/80145iDC561D53A8DFC927/image-size/large?v=v2&amp;px=999" role="button" title="RSS Feed.png" alt="RSS Feed.png" /></span></P> 2024-03-13T17:46:57.400000+01:00 https://community.sap.com/t5/technology-blogs-by-sap/how-to-add-a-new-entity-to-the-rap-bo-of-a-customizing-table/ba-p/13639320 How to add a new entity to the RAP BO of a customizing table 2024-03-15T14:18:55.104000+01:00 patrick_winkler https://community.sap.com/t5/user/viewprofilepage/user-id/729521 <H1 id="toc-hId-859956280">Introduction</H1><P><SPAN>You have created a RAP BO based on one or more customizing tables by using the&nbsp;</SPAN><A href="https://help.sap.com/docs/btp/sap-business-technology-platform/generating-business-configuration-maintenance-object-with-generate-abap-repository-objects-wizard" target="_blank" rel="noopener noreferrer">BC Maintenance Object ADT Wizard</A><SPAN>&nbsp;as described in&nbsp;</SPAN><A href="https://developers.sap.com/group.abap-env-factory.html" target="_blank" rel="noopener noreferrer">this tutorial</A><SPAN>.</SPAN></P><P>You want to extend the RAP BO by adding an additional customizing table to the data model.</P><P>The <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/business-configuration-maintenance-object?version=Cloud" target="_self" rel="noopener noreferrer">business configuration maintenance object</A> supports RAP BO whose height of the composition tree is less than or equal to 2.&nbsp;For example, root entity A has two child entities B and C. Entity C has a child entity D. D cannot have any further child entities.<BR />Depending on the back-end version, while the ADT wizard can handle multiple tables at the same time, the most complex supported scenarios require a manual enhancement after generation.</P><P>This blog is relevant for:</P><UL><LI><A class="" href="https://community.sap.com/t5/c-khhcw49343/SAP+BTP%25252C+ABAP+environment/pd-p/73555000100800001164" target="_blank">SAP BTP, ABAP environment</A><SPAN>&nbsp;</SPAN>&nbsp;</LI><LI><A class="" href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA+Private+Cloud/pd-p/5c26062a-9855-4f39-8205-272938b6882f" target="_blank">SAP S/4HANA Private Cloud</A></LI><LI><A class="" href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA+Public+Cloud/pd-p/08e2a51b-1ce5-4367-8b33-4ae7e8b702e0" target="_blank">SAP S/4HANA Public Cloud</A></LI></UL><P>Further reading:</P><UL><LI><A href="https://community.sap.com/t5/tag/business%20configuration%20maintenance%20object/tg-p/board-id/technology-blog-sap" target="_blank">Related blog posts</A></LI><LI>Learn how you can use<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap?url_id=text-sapcommunity-prdteng-ABAP" target="_blank" rel="noopener noreferrer">ABAP technology</A><SPAN>&nbsp;</SPAN>to develop innovative applications and business solutions across SAP’s portfolio on<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap" target="_blank" rel="noopener noreferrer">SAP Learning Site</A>.</LI></UL><H1 id="toc-hId-663442775">Example RAP BO</H1><P>You have used the following table ZCB_HEAD with the ADT wizard to generate a maintenance object:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Header' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #C @AbapCatalog.dataMaintenance : #ALLOWED define table zcb_head { key client : abap.clnt not null; key head_key : zcb_id not null; head_content : abap.char(30); last_changed_at : abp_lastchange_tstmpl; local_last_changed_at : abp_locinst_lastchange_tstmpl; }</code></pre><P>&nbsp;</P><P>The table ZCB_ITEM that you want to add has the following structure and is a child entity of table ZCB_HEAD:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Item' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #C @AbapCatalog.dataMaintenance : #ALLOWED define table zcb_item { key client : abap.clnt not null; @AbapCatalog.foreignKey.keyType : #KEY @AbapCatalog.foreignKey.screenCheck : false key head_key : zcb_id not null with foreign key zcb_head where client = zcb_item.client and head_key = zcb_item.head_key; key item_key : abap.numc(3) not null; item_content : abap.char(30); local_last_changed_at : abp_locinst_lastchange_tstmpl; }</code></pre><P>&nbsp;</P><H1 id="toc-hId-466929270">Data model &amp; behavior</H1><H3 id="toc-hId-528581203">CDS View</H3><P>Create a CDS view for table ZCB_ITEM with</P><UL><LI>an association to parent table ZCB_HEAD</LI><LI>an association to the singleton entity based on a calculated field SingletonID</LI><LI>if you do not have a projection layer, add the annotation&nbsp;@Metadata.allowExtensions: true</LI></UL><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Header' @AccessControl.authorizationCheck: #CHECK @Metadata.allowExtensions: true define view entity ZI_Item as select from zcb_item association to parent ZI_Header as _Header on $projection.HeadKey = _Header.HeadKey association [1..1] to ZI_Header_S as _HeaderAll on $projection.SingletonID = _HeaderAll.SingletonID { key head_key as HeadKey, key item_key as ItemKey, item_content as ItemContent, @Semantics.systemDateTime.localInstanceLastChangedAt: true @Consumption.hidden: true local_last_changed_at as LocalLastChangedAt, @Consumption.hidden: true 1 as SingletonID, _Header, _HeaderAll }</code></pre><P>&nbsp;</P><P>Add a composition to ZI_Item in the CDS View of table ZCB_HEAD:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Header' @AccessControl.authorizationCheck: #CHECK define view entity ZI_Header as select from zcb_head association to parent ZI_Header_S as _HeaderAll on $projection.SingletonID = _HeaderAll.SingletonID composition [0..*] of ZI_Item as _Item { key head_key as HeadKey, head_content as HeadContent, @Semantics.systemDateTime.lastChangedAt: true last_changed_at as LastChangedAt, @Semantics.systemDateTime.localInstanceLastChangedAt: true @Consumption.hidden: true local_last_changed_at as LocalLastChangedAt, @Consumption.hidden: true 1 as SingletonID, _HeaderAll, _Item }</code></pre><P>&nbsp;</P><P>If you have a projection layer, create a projection CDS view for ZI_Item with redirection to entity ZC_Header and the singleton entity. Save and activate.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #CHECK @Metadata.allowExtensions: true @EndUserText.label: 'Maintain Item' define view entity ZC_Item as projection on ZI_Item { key HeadKey, key ItemKey, ItemContent, @Consumption.hidden: true LocalLastChangedAt, @Consumption.hidden: true SingletonID, _Header : redirected to parent ZC_Header, _HeaderAll : redirected to ZC_Header_S }</code></pre><P>&nbsp;</P><P>If you have a projection layer, add a redirection to composition child ZI_Item in the projection CDS view of ZI_Header:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Maintain Header' @AccessControl.authorizationCheck: #CHECK @Metadata.allowExtensions: true define view entity ZC_Header as projection on ZI_Header { key HeadKey, HeadContent, LastChangedAt, @Consumption.hidden: true LocalLastChangedAt, @Consumption.hidden: true SingletonID, _HeaderAll : redirected to parent ZC_Header_S, _Item : redirected to composition child ZC_Item }</code></pre><P>&nbsp;</P><H2 id="toc-hId-202984979">Behavior definition</H2><P>In the behavior definition ZI_HEADER_S, add the following definition for entity ZI_Item:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>define behavior for ZI_Item alias Item persistent table ZCB_ITEM draft table ZCB_ITEM_D etag master LocalLastChangedAt lock dependent by _HeaderAll authorization dependent by _HeaderAll { field ( mandatory : create ) ItemKey; field ( readonly ) SingletonID, HeadKey, LocalLastChangedAt; field ( readonly : update ) ItemKey; field ( notrigger ) SingletonID, LocalLastChangedAt; update( features : global ); delete( features : global ); mapping for ZCB_ITEM { HeadKey = HEAD_KEY; ItemKey = ITEM_KEY; ItemContent = ITEM_CONTENT; LocalLastChangedAt = LOCAL_LAST_CHANGED_AT; } association _HeaderAll { with draft; } association _Header { with draft; } validation ValidateTransportRequest on save ##NOT_ASSIGNED_TO_DETACT { create; update; delete; } }</code></pre><P>&nbsp;</P><P>In the entity definition of ZI_Header, add an association to ZI_Item:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>association _Item { create ( features : global ); with draft; }</code></pre><P>&nbsp;</P><P>Save the behavior definition. Place the cursor on the draft table ZCB_ITEM_D and use the quick assist (Ctrl+1) to generate the draft table. After activating the draft table, activate the behavior definition.<BR />Place the cursor on ZI_Item underlined by a warning message and use the quick assist to generate the missing method implementations.</P><P>If you have a projection layer, adjust the behavior projection:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>projection; strict; use draft; define behavior for ZC_Header_S alias HeaderAll { use action Edit; use action Activate; use action Discard; use action Resume; use action Prepare; use action SelectCustomizingTransptReq; use association _Header { create; with draft; } } define behavior for ZC_Header alias Header { use update; use delete; use association _HeaderAll { with draft; } use association _Item { create; with draft; } } define behavior for ZC_Item alias Item { use update; use delete; use association _HeaderAll { with draft; } use association _Header { with draft; } }</code></pre><P>&nbsp;</P><H2 id="toc-hId-6471474">Behavior implementation</H2><P>Edit the behavior implementation class.&nbsp;<BR />Add the table entity relation for ZCB_ITEM:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>CLASS LHC_RAP_TDAT_CTS IMPLEMENTATION. METHOD GET. result = mbc_cp_api=&gt;rap_tdat_cts( tdat_name = 'ZHEADER' table_entity_relations = VALUE #( ( entity = 'Header' table = 'ZCB_HEAD' ) ( entity = 'Item' table = 'ZCB_ITEM' ) ) ) ##NO_TEXT. ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P>Implement the methods of LHC_ZI_ITEM and move the class definition LHC_RAP_TDAT_CTS up:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>CLASS lhc_rap_tdat_cts DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS: get RETURNING VALUE(result) TYPE REF TO if_mbc_cp_rap_tdat_cts. ENDCLASS. CLASS lhc_item DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_global_features FOR GLOBAL FEATURES IMPORTING REQUEST requested_features FOR Item RESULT result. METHODS ValidateTransportRequest FOR VALIDATE ON SAVE IMPORTING keys FOR Item~ValidateTransportRequest. ENDCLASS. CLASS lhc_item IMPLEMENTATION. METHOD get_global_features. DATA edit_flag TYPE abp_behv_flag VALUE if_abap_behv=&gt;fc-o-enabled. IF lhc_rap_tdat_cts=&gt;get( )-&gt;is_editable( ) = abap_false. edit_flag = if_abap_behv=&gt;fc-o-disabled. ENDIF. result-%update = edit_flag. result-%delete = edit_flag. ENDMETHOD. METHOD ValidateTransportRequest. DATA change TYPE REQUEST FOR CHANGE ZI_Header_S. SELECT SINGLE TransportRequestID FROM zcb_head_d_s WHERE SingletonID = 1 INTO (TransportRequestID). lhc_rap_tdat_cts=&gt;get( )-&gt;validate_changes( transport_request = TransportRequestID table = 'ZCB_ITEM' keys = REF #( keys ) reported = REF #( reported ) failed = REF #( failed ) change = REF #( change-item ) ). ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P>If you are using the VALIDATE_ALL_CHANGES method, delete the ValidateTransportRequest method from the LHC_ITEM class and adjust the ValidateTransportRequest method of the LHC_ZI_HEADER class:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>VALIDATETRANSPORTREQUEST FOR VALIDATE ON SAVE IMPORTING KEYS_HEADER FOR Header~ValidateTransportRequest KEYS_ITEM FOR Item~ValidateTransportRequest. [...] METHOD VALIDATETRANSPORTREQUEST. DATA change TYPE REQUEST FOR CHANGE ZI_Header_S. IF keys_Header IS NOT INITIAL. DATA(is_draft) = keys_Header[ 1 ]-%IS_DRAFT. ELSEIF keys_Item IS NOT INITIAL. is_draft = keys_Item[ 1 ]-%IS_DRAFT. ELSE. RETURN. ENDIF. READ ENTITY IN LOCAL MODE ZI_Header_S FROM VALUE #( ( %IS_DRAFT = is_draft SingletonID = 1 %CONTROL-TransportRequestID = if_abap_behv=&gt;mk-on ) ) RESULT FINAL(transport_from_singleton). IF lines( transport_from_singleton ) = 1. DATA(transport_request) = transport_from_singleton[ 1 ]-TransportRequestID. ENDIF. lhc_rap_tdat_cts=&gt;get( )-&gt;validate_all_changes( transport_request = transport_request table_validation_keys = VALUE #( ( table = 'ZCB_HEAD' keys = REF #( keys_Header ) ) ( table = 'ZCB_ITEM' keys = REF #( keys_Item ) ) ) reported = REF #( reported ) failed = REF #( failed ) change = REF #( change ) ). ENDMETHOD.</code></pre><P>&nbsp;</P><P>Add the %ASSOC-_Item statement to the GET_GLOBAL_FEATURES method of LHC_ZI_HEADER:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code> METHOD get_global_features. DATA edit_flag TYPE abp_behv_flag VALUE if_abap_behv=&gt;fc-o-enabled. IF lhc_rap_tdat_cts=&gt;get( )-&gt;is_editable( ) = abap_false. edit_flag = if_abap_behv=&gt;fc-o-disabled. ENDIF. result-%update = edit_flag. result-%delete = edit_flag. result-%ASSOC-_Item = edit_flag. ENDMETHOD.</code></pre><P>&nbsp;</P><H2 id="toc-hId--190042031">Metadata extension</H2><P>Create a metadata extension for ZI_Item or ZC_Item when using a projection layer:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CUSTOMER @UI: { headerInfo: { typeName: 'Item', typeNamePlural: 'Items', title: { type: #STANDARD, label: 'Item', value: 'ItemKey' } } } annotate entity ZC_Item with { @UI.identification: [ { position: 1 , label: 'ItemKey' } ] @UI.lineItem: [ { position: 1 , label: 'ItemKey' } ] @UI.facet: [ { id: 'ZI_Item', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'Item', position: 1 } ] ItemKey; @UI.identification: [ { position: 2 , label: 'ItemContent' } ] @UI.lineItem: [ { position: 2 , label: 'ItemContent' } ] ItemContent; }</code></pre><P>&nbsp;</P><P>Add a facet to _Item in the metadata extension of ZI_Header or ZC_Header when using a projection layer:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>[...] annotate entity ZC_Header with { @UI.identification: [ { position: 1 } ] @UI.lineItem: [ { position: 1 } ] @UI.facet: [ { id: 'ZI_Header', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'Header', position: 1 }, { id: 'ZI_Item', type: #LINEITEM_REFERENCE, label: 'Item', position: 2 , targetElement: '_Item' } ] HeadKey; [...]</code></pre><P>&nbsp;</P><H2 id="toc-hId--386555536">Access Control</H2><P>Create an access control object for ZI_Item:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@MappingRole: true define role ZI_Item { grant select on ZI_Item where INHERITING CONDITIONS FROM ENTITY ZI_Header; }</code></pre><P>&nbsp;</P><P>If you are using a projection layer, create an access control object for ZC_Item:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@MappingRole: true define role ZC_Item { grant select on ZC_Item where INHERITING CONDITIONS FROM ENTITY ZI_Item; }</code></pre><P>&nbsp;</P><H2 id="toc-hId--583069041">Transport Object</H2><P>Add table ZCB_ITEM to the transport object definition ZHEADER:</P><P>&nbsp;</P><pre class="lia-code-sample language-json"><code> "tableItems": [ { "tableName": "ZCB_HEAD", "isPrimaryTable": true }, { "tableName": "ZCB_ITEM" } ]</code></pre><P>&nbsp;</P><P>If you are using SAP S/4HANA On Premises 2023, use transaction SOBJ instead of ADT to adjust the transport object definition.</P><H2 id="toc-hId--779582546">Business Service</H2><P>Add entity ZC_Item to the service definition:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>define service ZUI_HEADER { expose ZC_Header_S as HeaderAll; expose ZC_Header as Header; expose ZC_Item as Item; }</code></pre><P>&nbsp;</P><H2 id="toc-hId--628841694">Maintenance Object</H2><P>No adjustments are necessary. You have the option to define specific table settings for the new entity, for example, to change the <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/table-creation-mode" target="_self" rel="noopener noreferrer">table creation mode</A>.</P><P>You can now restart the Custom Business Configurations app to review the changes.</P> 2024-03-15T14:18:55.104000+01:00 https://community.sap.com/t5/technology-blogs-by-sap/how-to-enable-an-odata-v4-service-for-anonymous-access/ba-p/13643572 How to enable an OData V4 service for anonymous access? 2024-03-19T21:57:57.546000+01:00 Andre_Fischer https://community.sap.com/t5/user/viewprofilepage/user-id/55 <H1 id="toc-hId-860703134">Introduction</H1><P>This requirement stems from a customer that asked how to publish an OData V4 service in an SAP S/4HANA on premise system such that it could be used on a public web site without the need to provide any authentication.</P><P>With OData V2 this requirement can be achieved more easily since here every service has it's own SICF node where it would be possible to store credentials just for this service.</P><P>In OData V4 we have only one SICF node, namely&nbsp;<STRONG>/sap/opu/odata4/.</STRONG></P><P>This is obviously a problem since there is no service specific node available in SICF.</P><H1 id="toc-hId-664189629">Solution</H1><P>The problem can be solved using the following approach.</P><OL><LI>Create a role that only contains the <STRONG>S_START</STRONG> authorization for the one single OData V4 service we want to publish ,&nbsp;<BR /><A href="https://help.sap.com/doc/saphelp_nw75/7.5.5/de-DE/c6/dd838722ce4b8d9cc4a0741d93d864/frameset.htm" target="_blank" rel="noopener noreferrer">help.sap.com/doc/saphelp_nw75/7.5.5/de-DE/c6/dd838722ce4b8d9cc4a0741d93d864/frameset.htm</A></LI><LI>Create a technical user that has only the authorization to access the single OData V4 service we want to make accessible.</LI><LI>Create an alias in SICF for the node&nbsp;<STRONG>/sap/opu/odata4/&nbsp;</STRONG></LI><LI>Store the credentials of the aforementioned user in the alias</LI><LI>Test the service&nbsp;&nbsp;</LI></OL><H1 id="toc-hId-467676124">Result</H1><P>As we can see accessing the service&nbsp;<STRONG>zrap630ui_shop_o4_05a</STRONG> via the alias <STRONG>zodatav4_2&nbsp;</STRONG>works.</P><P><A href="https://10.79.21.221:44301/sap/opu/zodatav4/sap/zrap630ui_shop_o4_05a/srvd/sap/zrap630ui_shop_o4_05a/0001/?sap-client=100" target="_blank" rel="noopener nofollow noreferrer">https://10.79.21.221:44301/sap/opu/zodatav4/sap/zrap630ui_shop_o4_05a/srvd/sap/zrap630ui_shop_o4_05a/0001/?sap-client=100</A></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="100_test_service_1.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83418i76C5523CCA831AAC/image-size/large?v=v2&amp;px=999" role="button" title="100_test_service_1.png" alt="100_test_service_1.png" /></span></P><P>Whereas accessing a second OData V4 service&nbsp;<STRONG>zrap630ui_shop_o4_05b</STRONG> via the alias <STRONG>zodatav4_2&nbsp;</STRONG>does <STRONG>NOT work</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="110_test_service_1.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83419i3EE98F3B6179053B/image-size/large?v=v2&amp;px=999" role="button" title="110_test_service_1.png" alt="110_test_service_1.png" /></span></P><P>This has been achieved by assigne the follwoing role the technical user which only contains the authorization to start the first service based on the authorization object <STRONG>S_START</STRONG>.</P><P style=" padding-left : 30px; "><STRONG>Please note:</STRONG><BR />Since OData V4 service use the authorization object S_START which is based on the service name it would for example be possible to publish several services that are in the same name range like</P><P style=" padding-left : 30px; ">zrap630ui_shop_o4_<STRONG>05A</STRONG>,&nbsp;zrap630ui_shop_o4_<STRONG>05B</STRONG>, ...&nbsp;zrap630ui_shop_o4_<STRONG>05Z</STRONG>,&nbsp;&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="120_role_2.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83420i0B3411C8E6E88B6F/image-size/large?v=v2&amp;px=999" role="button" title="120_role_2.png" alt="120_role_2.png" /></span></P><H1 id="toc-hId-271162619"><STRONG>How to section</STRONG></H1><H3 id="toc-hId-332814552"><STRONG>Create the role</STRONG></H3><P>Using PFCG and the role template <SPAN class="">/IWBEP/RT_MGW_USR we create a role as shown in the screen shot above that only contains the S_START authorization of one OData V4 service called&nbsp;ZRAP630UI_SHOP_O4_05A.<BR /></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="120_role_1.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83433iF66B19D135CCA30E/image-size/large?v=v2&amp;px=999" role="button" title="120_role_1.png" alt="120_role_1.png" /></span></P><H3 id="toc-hId-136301047"><STRONG>Create</STRONG> the<STRONG> user</STRONG></H3><P>As a user we create a technical user whose credentials will be stored in the system alias.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="130_technical_user.png" style="width: 876px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83436iC507E93B64E11FAF/image-size/large?v=v2&amp;px=999" role="button" title="130_technical_user.png" alt="130_technical_user.png" /></span></P><H3 id="toc-hId--60212458"><STRONG>Create the system alias</STRONG></H3><P>1. We start by right-clicking on the node <STRONG>opu</STRONG> and choose <STRONG>New subelement</STRONG>.</P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="010_Create_Alias.png" style="width: 643px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83424i3B2863743E4C897D/image-size/large?v=v2&amp;px=999" role="button" title="010_Create_Alias.png" alt="010_Create_Alias.png" /></span></STRONG></P><P>2. We create a new service element called <STRONG>ZODATAV4_2&nbsp;</STRONG>and choose the option <STRONG>Alias to an existing service</STRONG>.</P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="020_create_alias.png" style="width: 877px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83423i28731E07AD83EAC7/image-size/large?v=v2&amp;px=999" role="button" title="020_create_alias.png" alt="020_create_alias.png" /></span></STRONG></P><P>3. We choose the tab <STRONG>Target</STRONG>, select the node <STRONG>OdataV4</STRONG> by double-clicking on it.</P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="030_create_alias.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83426i2C2315451CB01CBA/image-size/large?v=v2&amp;px=999" role="button" title="030_create_alias.png" alt="030_create_alias.png" /></span></STRONG></P><P>4. We navigate to the tab <STRONG>Logon Data</STRONG>, choose <STRONG>Alternative Logon Procedure</STRONG> and enter the credentials of our service user.</P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="050_create_alias.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83427iF7847BF82D023A46/image-size/large?v=v2&amp;px=999" role="button" title="050_create_alias.png" alt="050_create_alias.png" /></span></STRONG></P><P>5. We navigate down and remove all logon procedures beside <STRONG>Logon using service data</STRONG>.</P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="060_create_alias.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83428i4185C25916590275/image-size/large?v=v2&amp;px=999" role="button" title="060_create_alias.png" alt="060_create_alias.png" /></span></STRONG></P><P>6. Do not forget to activate the link in SICF.</P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="070_create_alias.png" style="width: 484px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83429i1BBD6FF69034F2EA/image-size/large?v=v2&amp;px=999" role="button" title="070_create_alias.png" alt="070_create_alias.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="080_create_alias.png" style="width: 747px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83430i0F969188176ECB1E/image-size/large?v=v2&amp;px=999" role="button" title="080_create_alias.png" alt="080_create_alias.png" /></span></STRONG></P><P>7. Check the result</P><P><STRONG><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="090_create_alias.png" style="width: 909px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/83431i5366E830240FD49A/image-size/large?v=v2&amp;px=999" role="button" title="090_create_alias.png" alt="090_create_alias.png" /></span></STRONG></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2024-03-19T21:57:57.546000+01:00 https://community.sap.com/t5/technology-blogs-by-sap/how-to-get-a-quot-display-only-quot-fiori-app-from-a-quot-manage-quot-fiori/ba-p/13645765 How to get a "Display only" Fiori app from a "Manage" Fiori app 2024-03-22T09:32:32.218000+01:00 mlauber https://community.sap.com/t5/user/viewprofilepage/user-id/157846 <P>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".</P><P>&nbsp;</P><H1 id="toc-hId-860764610">Example Case</H1><P>In this example I'll be using "Manage Sales Order" app (<SPAN>F3893)</SPAN>:&nbsp;<A href="https://fioriappslibrary.hana.ondemand.com/sap/fix/externalViewer/#/detail/Apps('F3893')/S28OP" target="_blank" rel="noopener nofollow noreferrer">Manage Sales Orders - Version 2</A>&nbsp;</P><P>&nbsp;</P><H1 id="toc-hId-664251105">CDS Development</H1><H2 id="toc-hId-596820319">Step 1: Get Main CDS</H2><P>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:&nbsp;<STRONG>C_SALESORDERMANAGE_SRV</STRONG>. We open this object in ADT and soon find the main CDS:&nbsp;<STRONG>C_SalesOrderManage</STRONG>.</P><P>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 <A href="https://community.sap.com/t5/technology-blogs-by-sap/can-i-extend-a-certain-app-with-clean-core-how/ba-p/13627922" target="_blank">there</A>. If you still need help, maybe I make a new blog for that topic alone, we'll see.</P><H2 id="toc-hId-400306814">Step 2: Decide Approach (yes, there is more than one)</H2><P>Now that we have&nbsp;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 <STRONG>root view entity</STRONG> which has <STRONG>composition children</STRONG>. So we can't just select from&nbsp;R_SalesOrderTP in our own CDS and reuse the compositions. For example&nbsp;R_SalesOrderItemTP is linked to&nbsp;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:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_0-1711023603352.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84408iB61B764D3419620E/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_0-1711023603352.png" alt="mlauber_0-1711023603352.png" /></span></P><P>We can see that for "as select from" we have&nbsp;<SPAN><STRONG>I_SalesOrder</STRONG>. 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.</SPAN></P><P>At this point we should make a choice:</P><UL><LI>Do we want to have full functionality as in the original app, for example navigating from a sales order details page, all the way into a sales order item details page?</LI><LI>Or is it enough with one list report and one detail page? Namely in this example, a way to search for sales orders via filters, then clicking on a sales order and display all wanted details, but no further navigation.</LI></UL><P>Important: both are possible.</P><UL><LI>Choice one, full functionality: this means more wrappers and more work. You will basically create your own root view entity, with your own composite child entities (all wrapping around standard ones so you don't create them from scratch, thus called wrapper) for all the data you wish to display in your display-only app. You will then create the proper projection view, just like&nbsp;C_SalesOrderManage, create service definition and binding, which then gives you your OData service for the Fiori app.</LI><LI>Choice two: if we just need a simple list report with details page, we can generate an OData service directly from a CDS view entity. In this example I will go with this option, for simpleness sake. But basically the steps are as said just above, creating a wrapper for all CDS you want in the end and creating a proper OData service with RAP and ABAP Cloud.</LI></UL><H2 id="toc-hId-203793309">Step 3: Create wrapper for CDS</H2><OL><LI>Create a new data definition:&nbsp;ZC_SalesOrderTP (we give it C in its name for "consumption" because we will directly expose this CDS as OData service, and "TP" for transactional processing).</LI><LI>As reference CDS view, be sure to enter&nbsp;<SPAN><STRONG>I_SalesOrder</STRONG></SPAN> as identified in the previous step. If you do, ADT will add all fields and associations for you.</LI><LI>First off, at the beginning of your view entity you will need below two semantics. The first will make sure we don't just blindly take over all semantics from I_SalesOrder (this could be handy and potentially faster, but since we may not use absolutely all associations etc., it is in my opinion better to recreate your own semantics and thus the syntax checker will also help you to make sure everything is correct). The second one allows us to create an extra metadata extension file, which we can use for all UI annotations, which makes the whole thing less messy. I always recommend to separate UI annotations from other semantics.</LI></OL><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true​</code></pre><P>&nbsp;</P><UL><LI>Next we add that we want to publish this CDS as OData service (with <STRONG>@OData.publish: true</STRONG>). This is only needed if you are going the more simple option. Otherwise you leave this out and create the OData service via Service Definition and Binding, once you built the complete RAP object.</LI><LI>Next I copied some of the semantics from the standard CDS and here is the complete start of my CDS so far:<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_1-1711024804490.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84415i375EC8133FBB4288/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_1-1711024804490.png" alt="mlauber_1-1711024804490.png" /></span></LI><LI>Add the associations you want to use (again, simply copy from standard CDS) - if you are doing the more functionality route, be sure to use composite child instead of association and you have to create z-wrappers for each, and use the z-CDS here, not for example R_SalesOrderItemTP:</LI></UL><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>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​</code></pre><P>&nbsp;</P><UL><LI>Now I do the same per field that is important: add semantics to have a good UX. Here a couple of examples (I won't add everything as it would just get too big):</LI></UL><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@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,​</code></pre><P>&nbsp;</P><P>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.</P><H2 id="toc-hId-7279804">Step 4: Authorization</H2><P>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.</P><P>In ADT, create a new Access Control with the same name as your CDS you want to be checked, in my case <STRONG>ZC_SalesOrderTP</STRONG>. Then simply enter the "inheriting conditions from entity", save and done:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@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; }</code></pre><P>&nbsp;</P><H2 id="toc-hId--189233701">Step 5: UI Annotations for Fiori</H2><P>I recommend to add all UI annotations that you want Fiori display via a <STRONG>metadata extension</STRONG> file. In ADT, create a metadata extension file that has the exact same name as your CDS, in my case&nbsp;<STRONG>ZC_SalesOrderTP</STRONG>. Here once again I opened the standard metadata extension file with <EM>Ctrl + Shift + A</EM> 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.</P><P>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:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>{ id: 'CompletionStatus', label: 'Completion Status', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, parentId: 'StatusTab', importance: #HIGH, position: 30, targetQualifier: 'CompletionStatus' },</code></pre><P>&nbsp;</P><P>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:&nbsp;<STRONG>parentId: 'StatusTab'</STRONG>. This means our&nbsp;CompletionStatus facet is inside another, a parent facet. If you now didn't copy that parent facet, and there is no facet with id&nbsp;StatusTab, then your Fiori application will not work properly, because it cannot resolve this.</P><P>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.</P><H2 id="toc-hId--385747206">Step 6: Publish OData Service</H2><P>Next we need to publish our custom OData service with transaction <STRONG>/IWFND/MAINT_SERVICE</STRONG>&nbsp;for OData V2 services or&nbsp;<STRONG>/IWFND/V4_ADMIN</STRONG> for OData V4. Note that when using&nbsp;<STRONG>@OData.publish: true</STRONG>&nbsp;the system will add <STRONG>_CDS</STRONG> to your OData service name. Confirm your service and metadata was loaded successfully before continuing.&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_2-1711025801015.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84468i9457B0D8FAAA7A3D/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_2-1711025801015.png" alt="mlauber_2-1711025801015.png" /></span></P><P>&nbsp;</P><H1 id="toc-hId--711343430">Fiori Development</H1><P>Next up, we create our display-only Fiori elements app.</P><P>Open <STRONG>Business Application Studio (BAS)</STRONG>&nbsp;on your SAP BTP and start a space for Fiori development.</P><P>Create a new Project from Template and choose <STRONG>SAP Fiori application</STRONG>. 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 <STRONG>Destination</STRONG> to that S/4HANA system via Cloud Connector, and then I select my OData service:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_4-1711027035961.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84482i4FA78DA730C70195/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_4-1711027035961.png" alt="mlauber_4-1711027035961.png" /></span></P><P>Next, we enter the project details, for example:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_3-1711026913484.png" style="width: 421px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84481i6D0CE7D2C93ACD34/image-dimensions/421x408?v=v2" width="421" height="408" role="button" title="mlauber_3-1711026913484.png" alt="mlauber_3-1711026913484.png" /></span></P><P>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.</P><P>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:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_5-1711027196571.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84484iB0EDAC5EE4ACC560/image-size/medium?v=v2&amp;px=400" role="button" title="mlauber_5-1711027196571.png" alt="mlauber_5-1711027196571.png" /></span></P><P>Once the wizard is done, simply test your app. Because we have done all the UI annotations, you should be good to go.</P><P>Deploy your app and we are almost done.</P><P>&nbsp;</P><H1 id="toc-hId--907856935">Adding your App to the Fiori Launchpad</H1><P>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:</P><OL><LI>Create a technical catalog if you don't have a suitable one using&nbsp;<STRONG>Launchpad App Manager - Cross Client</STRONG></LI><LI>From your technical catalog, choose Add App to add an "app descriptor item", or Tile and Target Mapping in other words (so we are still in&nbsp;<STRONG>Launchpad App Manager - Cross Client</STRONG>&nbsp;we have created/navigated to our technical catalog, in edit mode and choose Add App)</LI><LI>Here under application component ID you have to enter the full application name you chose for your Fiori application when you first created it in BAS. In my case i had namespace "disp" and name "salesorderdisplay", meaning&nbsp;<STRONG>disp.salesorderdisplay</STRONG>. If you enter this, the app should fetch all the necessary FLP config you already did in BAS (it will also fetch ICF path for you):<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_6-1711027655971.png" style="width: 449px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84488iD75BB6F456ED30CE/image-dimensions/449x249?v=v2" width="449" height="249" role="button" title="mlauber_6-1711027655971.png" alt="mlauber_6-1711027655971.png" /></span></LI><LI>In my case, because I chose "Mange Sales Orders" app, I also added all the parameters which that app uses - be sure not to forget this!<BR /><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_7-1711027746195.png" style="width: 471px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84490i42CC88DECF8039E5/image-dimensions/471x177?v=v2" width="471" height="177" role="button" title="mlauber_7-1711027746195.png" alt="mlauber_7-1711027746195.png" /></span></LI><LI>Save this.</LI><LI>Next, we need to add the app to a business catalog (maybe create a new business catalog if needed) or several, depending on your setup</LI><LI>Then we need to add our business catalog(s) for this app to all the appropriate roles who should have this display-only app</LI><LI>Lastly, if the app should be on a Space and Page, we need to do that config</LI></OL><P>All done! You have a display-only app, using standard CDS which you have wrapped into a custom OData service, for a custom app.&nbsp;</P><P>And if you were wondering; <STRONG>Is this Clean Core</STRONG>? 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).</P><P>&nbsp;</P><H1 id="toc-hId--334630357">Solution</H1><P>The final app - list report page (including smart links to customer, without us having to code it ourselves):</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_8-1711029611078.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84511i7B3CFAEE95DF61C2/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_8-1711029611078.png" alt="mlauber_8-1711029611078.png" /></span></P><P>Details/Object page:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="mlauber_9-1711029665573.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/84512i1B4327EEC7AF22AD/image-size/large?v=v2&amp;px=999" role="button" title="mlauber_9-1711029665573.png" alt="mlauber_9-1711029665573.png" /></span></P><P>Hope this was helpful! Let me know if you wonder anything.</P> 2024-03-22T09:32:32.218000+01:00 https://community.sap.com/t5/application-development-blog-posts/step-by-step-process-to-publish-standard-business-events-from-s-4-hana-to/ba-p/13650346 Step by Step process to publish standard Business events from S/4 Hana to Event Mesh 2024-03-27T10:04:35.331000+01:00 BramhaniG https://community.sap.com/t5/user/viewprofilepage/user-id/566391 <P>Hi All,</P><P>This blog explains Outbound Configuration to send events from S/4 Hana on premise to Event Mesh and test the same.</P><P>You need to have below roles</P><P>1. SAP_IWXBE_RT_XBE_ADM</P><P>2. SAP_IWXBE_RT_XBE_BUSI</P><P>3. SAP_IWXBE_RT_XBE_MDT</P><P>In S/4 system, please follow below steps.</P><P>Create a Channel using T-code “/n/IWXBE/CONFIG”.</P><P>Click on “Via Service key”.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_0-1711475041180.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86881iE637E0E22D5CA546/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_0-1711475041180.png" alt="bramhani_0-1711475041180.png" /></span></P><P>Enter Channel name and Description. Get the Event mesh instance key from Basis team. Copy and paste the service key of event mesh as shown below.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_2-1711475317479.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86883i2A4F0859B8DB1D4A/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_2-1711475317479.png" alt="bramhani_2-1711475317479.png" /></span></P><P>select the channel and activate it.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_3-1711475479532.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86886i0EB2C5A2F4B43AD2/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_3-1711475479532.png" alt="bramhani_3-1711475479532.png" /></span></P><P>Once it is activated, click on Check connection. You will get the below message if connection successfull.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_4-1711475533545.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86889i3FFEC427FB94E325/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_4-1711475533545.png" alt="bramhani_4-1711475533545.png" /></span></P><P>To Create Outbound binding, please select channel and click on outbound bindings.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_5-1711475580760.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86891i82A06E121ECDF01D/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_5-1711475580760.png" alt="bramhani_5-1711475580760.png" /></span></P><P>Click on create and then F4.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_6-1711475600642.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86893i2C12F131954216BE/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_6-1711475600642.png" alt="bramhani_6-1711475600642.png" /></span></P><P>Select the topic from F4 and save it. If you don't find topics in F4 help, you need to implement note "<SPAN><SPAN class="">3346777".</SPAN></SPAN></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_7-1711475621319.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86895i97F762B74645C5E3/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_7-1711475621319.png" alt="bramhani_7-1711475621319.png" /></span></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_8-1711475708928.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86898i057971A6E824C6A4/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_8-1711475708928.png" alt="bramhani_8-1711475708928.png" /></span></P><P>&nbsp;</P><P>Now, you need to create a Queue in Event Mesh and subscribe the topic.</P><P>Open Event Mesh and click on Create Queue.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_1-1711476609138.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86913iB63510AA9161AA13/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_1-1711476609138.png" alt="bramhani_1-1711476609138.png" /></span></P><P>Provide some name and click on create</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_3-1711476811030.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86915iCB42CD50A1CDE183/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_3-1711476811030.png" alt="bramhani_3-1711476811030.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_5-1711476924298.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86918iB3F08499FD8C2378/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_5-1711476924298.png" alt="bramhani_5-1711476924298.png" /></span></P><P>Once the Queue is created, click on highlighted button and select Queue Subscriptions.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_6-1711476985619.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86919i396F8A8C4D570A86/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_6-1711476985619.png" alt="bramhani_6-1711476985619.png" /></span></P><P>Enter Topic name along with the namespace and click on add button to subscribe to the topic.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_8-1711477345992.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86924i50BC9AE2017C70C9/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_8-1711477345992.png" alt="bramhani_8-1711477345992.png" /></span></P><P>Configuration is done in S/4 Hana system and in the BTP Event Mesh.</P><P>To test the messages,&nbsp;</P><P>Trigger Standard Business Partner Events by creating a business partner using T-Code BP.</P><P>Click on Person.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_9-1711477440402.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86925iF2B92A1E6E5521EA/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_9-1711477440402.png" alt="bramhani_9-1711477440402.png" /></span></P><P>Enter First name and last name and then save it.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_10-1711477470527.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86927i46D3C3750E83F7BD/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_10-1711477470527.png" alt="bramhani_10-1711477470527.png" /></span></P><P>Message has been sent to Event Mesh.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_11-1711477705205.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86929iE0711F544C6161C3/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_11-1711477705205.png" alt="bramhani_11-1711477705205.png" /></span></P><P>Check the payload after consuming the message in CPI by integration team.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_12-1711477855623.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86931iCC6780B78A7A4A84/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_12-1711477855623.png" alt="bramhani_12-1711477855623.png" /></span></P><P>Message number decreased to zero in Event Mesh after consuming the message in CPI.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="bramhani_13-1711477967113.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/86933iDD26774AAA460556/image-size/medium?v=v2&amp;px=400" role="button" title="bramhani_13-1711477967113.png" alt="bramhani_13-1711477967113.png" /></span></P><P>Happy to hear the feedback&nbsp;<span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2024-03-27T10:04:35.331000+01:00 https://community.sap.com/t5/technology-blogs-by-members/upload-excel-using-sap-rap-only/ba-p/13666565 Upload Excel using SAP RAP Only 2024-04-16T12:50:21.211000+02:00 Amit_Sharma https://community.sap.com/t5/user/viewprofilepage/user-id/1434841 <P>Develop an SAP RAP based App to upload excel (CSV) file just like you used to develop SE38 classical report.</P><P>We often get this requirement of developing a utility to create/update mass data for a BO such as Purchase Orders, Materials, Sales Orders etc. In S/4 HANA, the customer prefers to use SAP Fiori applications, rather than using the old classical reports. But can we develop a Fiori application to upload a file without involving using SAP UI5? Yes, we can.</P><P>Using the following annotations, we can attach any file to our BO in SAP RAP –</P><P><STRONG>@Semantics.largeObject</STRONG></P><P>The only constraint is that we would need a table holding the uploaded attachment large object. Let’s dive into the steps –</P><P>First, we will create database tables. We need to create two DB tables. The first table i.e. parent table will store the file attachment, and the second table i.e. child table will store the data from the uploaded file. In our scenario, we will be uploading and processing CSV file.</P><P>File table - ZSES_FILE_TABLE</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'User File Table' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zses_file_table { key client : abap.clnt not null; key end_user : uname not null; status : abap.char(1); attachment : xstringval; mimetype : abap.char(128); filename : abap.char(128); local_created_by : abp_creation_user; local_created_at : abp_creation_tstmpl; local_last_changed_by : abp_locinst_lastchange_user; local_last_changed_at : abp_locinst_lastchange_tstmpl; last_changed_at : abp_lastchange_tstmpl }</code></pre><P>&nbsp;</P><P>Excel data table - ZSES_DB</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Excel Data' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #ALLOWED define table zses_db { key mandt : mandt not null; key end_user : uname not null; key entrysheet : lblni not null; key ebeln : ebeln not null; key ebelp : ebelp not null; ext_number : lblne1; begdate : lzvon; enddate : lzbis; @Semantics.quantity.unitOfMeasure : 'zses_db.base_uom' quantity : mengev; base_uom : meins; fin_entry : final; error : boolean; error_message : char100; }</code></pre><P>&nbsp;</P><P>Now, let's create Interface views on top of the tables we just created.</P><P>For parent interface view, we create ZI_SES_PARENT.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Excel File Table' define root view entity zi_ses_parent as select from usr02 as _user left outer join zses_file_table as _ses_file on _user.bname = _ses_file.end_user composition [0..*] of zi_ses_excel_data as _ses_excel { key _user.bname as end_user, _ses_file.status as status, cast( case when _ses_file.filename is initial and _ses_file.status is initial then 'File Not Uploaded' when _ses_file.filename is not initial and _ses_file.status is initial then 'File Uploaded' when _ses_file.filename is initial then 'File Not Uploaded' when _ses_file.status is not initial then 'File Processed' else ' ' end as abap.char( 20 ) ) as FileStatus, cast( case when _ses_file.filename is initial and _ses_file.status is initial then '1' when _ses_file.filename is not initial and _ses_file.status is initial then '2' when _ses_file.filename is initial then '1' when _ses_file.status is not initial then '3' else ' ' end as abap.char( 1 ) ) as CriticalityStatus, cast( case when _ses_file.filename is not initial then ' ' else 'X' end as boolean preserving type ) as HideExcel, @Semantics.largeObject: { mimeType: 'MimeType', fileName: 'Filename', acceptableMimeTypes: [ 'text/csv' ], contentDispositionPreference: #INLINE } // This will store the File into our table _ses_file.attachment as Attachment, @Semantics.mimeType: true _ses_file.mimetype as MimeType, _ses_file.filename as Filename, @Semantics.user.createdBy: true _ses_file.local_created_by as Local_Created_By, @Semantics.systemDateTime.createdAt: true _ses_file.local_created_at as Local_Created_At, @Semantics.user.lastChangedBy: true _ses_file.local_last_changed_by as Local_Last_Changed_By, //local ETag field --&gt; OData ETag @Semantics.systemDateTime.localInstanceLastChangedAt: true _ses_file.local_last_changed_at as Local_Last_Changed_At, //total ETag field @Semantics.systemDateTime.lastChangedAt: true _ses_file.last_changed_at as Last_Changed_At, _ses_excel } where _user.bname = $session.user</code></pre><P>&nbsp;</P><P>Looks complicated? Let me explain -<BR /><BR />1. We select primary data from USR02 table and left outer join it with our parent table&nbsp;ZSES_FILE_TABLE, so that every user can use the app without worrying about locking. As this application serves as a utility for excel file upload, multiple users can use it simultaneously.<BR />2. FileStatus field is introduced to decorate the application and show state to the user whether the file is uploaded, processed or not.<BR /><BR />Then we create interface view of child entity as ZI_SES_EXCEL_DATA.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'SES_excel_data' @Metadata.allowExtensions: true define view entity zi_ses_excel_data as select from zses_db association to parent zi_ses_parent as _ses_file on $projection.end_user = _ses_file.end_user { key end_user as end_user, key zses_db.entrysheet as Entrysheet, key zses_db.ebeln as Ebeln, key zses_db.ebelp as Ebelp, zses_db.ext_number as Ext_Number, zses_db.begdate as Begdate, zses_db.enddate as Enddate, zses_db.quantity as Quantity, zses_db.base_uom as Base_Uom, zses_db.fin_entry as Fin_Entry, zses_db.error as Error, zses_db.error_message as Error_Message, _ses_file }</code></pre><P>&nbsp;</P><P>Now, let's go for consumption views. These are pretty straightforward.</P><P>For parent, we create ZC_SES_PARENT.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Consumption View for File' @AccessControl.authorizationCheck: #NOT_REQUIRED @Metadata.allowExtensions: true define root view entity zc_ses_parent provider contract transactional_query as projection on zi_ses_parent { key end_user, @EndUserText.label: 'Processing Status' FileStatus as status, Attachment, MimeType, Filename, Local_Created_By, Local_Created_At, Local_Last_Changed_By, @EndUserText.label: 'Last Action On' Local_Last_Changed_At, Last_Changed_At, CriticalityStatus, HideExcel, /* Associations */ _ses_excel : redirected to composition child zc_ses_excel }</code></pre><P>&nbsp;</P><P>For child, we create&nbsp;ZC_SES_EXCEL.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Consumption View for Ses Excel Data' @AccessControl.authorizationCheck: #NOT_REQUIRED @Metadata.allowExtensions: true define view entity zc_ses_excel as projection on zi_ses_excel_data { key end_user, key Entrysheet, key Ebeln, key Ebelp, Ext_Number, Begdate, Enddate, Quantity, Base_Uom, Fin_Entry, Error, Error_Message, /* Associations */ _ses_file : redirected to parent zc_ses_parent }</code></pre><P>&nbsp;</P><P>We then create Metadata Extensions for both of our consumption/projection views -</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE annotate entity zc_ses_parent with { @UI.facet: [ /* Header Fecets and Datapoints */ { purpose: #HEADER, id:'HDR_USER', type: #DATAPOINT_REFERENCE, position: 10, targetQualifier: 'end_user' }, { purpose: #HEADER, id:'HDR_FILE', type: #DATAPOINT_REFERENCE, position: 20, targetQualifier: 'Local_Last_Changed_At' }, { purpose: #HEADER, id:'HDR_STATUS', type: #DATAPOINT_REFERENCE, position: 30, targetQualifier: 'status' }, //**---- Body facets { label: 'File Information', id: 'Attachment', type: #COLLECTION, position: 10 }, { label: 'Invoice Details', id: 'Invoicedet', type: #IDENTIFICATION_REFERENCE, position: 10, parentId: 'File', purpose: #STANDARD }, { id: 'Upload', type: #FIELDGROUP_REFERENCE, position: 20 ,targetQualifier: 'Upload', parentId: 'Attachment', purpose: #STANDARD }, //** --- Excel data Facet ** { label: 'Excel Data', id: 'Data', type: #LINEITEM_REFERENCE, position: 30, targetElement: '_ses_excel', parentId: 'Attachment', purpose: #STANDARD } ]// , hidden: #(HideExcel) } ] @UI: { lineItem: [ { position: 10, importance: #HIGH , label: 'Person Responsible'} ] , identification: [ { position: 10 , label: 'Person Responsible' } ], // { type: #FOR_ACTION, dataAction: 'uploadExcelData', label: 'Validate and Show' } ] , dataPoint: { title: 'Responsible Person', targetValueElement: 'end_user' } } end_user; @UI: { lineItem: [ { position: 20, importance: #HIGH , label: 'Processing Status'} ] , identification: [ { position: 20 , label: 'Processing Status' } ] , dataPoint: { title: 'Processing Status', targetValueElement: 'status' ,criticality: 'CriticalityStatus' ,criticalityRepresentation: #WITHOUT_ICON} } status; @UI: { fieldGroup: [ { position: 50, qualifier: 'Upload' , label: 'Attachment'} ]} @UI: { identification: [ { position: 30 , label: 'File' } ] } Attachment; @UI.hidden: true MimeType; @UI.hidden: true Filename; @UI: { dataPoint:{ title: 'Last Action On', targetValueElement: 'Local_Last_Changed_At' } } Local_Last_Changed_At; }</code></pre><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #CORE annotate entity zc_ses_excel with { @UI.facet: [{ id: 'SES', type: #FIELDGROUP_REFERENCE, purpose: #STANDARD, label: 'SES', targetQualifier: 'SESDetails' }] @UI.lineItem: [{ position: 1 }] end_user; @UI.lineItem: [{ position: 10 , label: 'SES Number', invocationGrouping: #ISOLATED }, { type: #FOR_ACTION, dataAction: 'createSES', label: 'Process SES' }] @UI.fieldGroup: [{ position: 10 , label: 'SES Number', qualifier: 'SESDetails' }] Entrysheet; @UI.lineItem: [{ position: 20 , label: 'PO' }] @UI.fieldGroup: [{ position: 20 , label: 'PO', qualifier: 'SESDetails' }] Ebeln; @UI.lineItem: [{ position: 30 , label: 'PO Item' }] @UI.fieldGroup: [{ position: 30 , label: 'PO Item', qualifier: 'SESDetails' }] Ebelp; @UI.lineItem: [{ position: 40 , label: 'SES Name' }] @UI.fieldGroup: [{ position: 40 , label: 'SES Name', qualifier: 'SESDetails' }] Ext_Number; @UI.lineItem: [{ position: 50 , label: 'Valid From' }] @UI.fieldGroup: [{ position: 50 , label: 'Valid From', qualifier: 'SESDetails' }] Begdate; @UI.lineItem: [{ position: 60 , label: 'Valid To' }] @UI.fieldGroup: [{ position: 60 , label: 'Valid To', qualifier: 'SESDetails' }] Enddate; @UI.lineItem: [{ position: 70 , label: 'Quantity' }] @UI.fieldGroup: [{ position: 70 , label: 'Quantity', qualifier: 'SESDetails' }] Quantity; @UI.lineItem: [{ position: 80 , label: 'UOM' }] @UI.fieldGroup: [{ position: 80 , label: 'UOM', qualifier: 'SESDetails' }] Base_Uom; @UI.lineItem: [{ position: 90 , label: 'Final Entry' }] @UI.fieldGroup: [{ position: 90 , label: 'Final Entry', qualifier: 'SESDetails' }] Fin_Entry; @UI.lineItem: [{ position: 100 , label: 'Error' , hidden: true}] @UI.fieldGroup: [{ position: 100 , label: 'Error' , hidden: true}] Error; @UI.lineItem: [{ position: 110 , label: 'Error Message' }] @UI.fieldGroup: [{ position: 110 , label: 'Error Message', qualifier: 'SESDetails' }] Error_Message; }</code></pre><P>&nbsp;</P><P>Now, we create behavior definition ZI_SES_PARENT -</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_i_ses_parent unique; strict ( 2 ); with draft; define behavior for zi_ses_parent alias File persistent table zses_file_table lock master total etag end_user draft table zses_file_tabled authorization master ( instance ) etag master end_user { create; update; delete; // Logic to convert uploaded excel into internal table and save to the child entity is written here action ( features : instance ) uploadExcelData result [1] $self; association _ses_excel { create; with draft; } // Logic to trigger action uploadExcelData determination fields on modify { field Filename ; } draft action Edit ; draft action Activate; draft action Discard; draft action Resume; draft determine action Prepare ; } define behavior for zi_ses_excel_data alias ExcelData persistent table ZSES_DB lock dependent by _ses_file draft table zses_dbd authorization dependent by _ses_file etag master Begdate { update; delete; field ( readonly ) end_user; association _ses_file { with draft; } // Logic to process the uploaded data from excel action createSES result [1] $self; }</code></pre><P>And the behavior projection ZC_SES_PARENT -&nbsp;</P><pre class="lia-code-sample language-abap"><code>projection; strict ( 2 ); use draft; define behavior for zc_ses_parent //alias &lt;alias_name&gt; { use update; use delete; field ( readonly ) status , Local_Last_Changed_At ; use action Edit; use action Activate; use action Discard; use action Resume; use action Prepare; use association _ses_excel { create; with draft; } } define behavior for zc_ses_excel //alias &lt;alias_name&gt; { use update; use delete; use association _ses_file { with draft; } use action createSES; }</code></pre><P>&nbsp;</P><P>Now, in the behavior implementation class -&nbsp;ZBP_I_SES_PARENT, we have two important methods -</P><P><STRONG><U>Determination method - 'Fields' -&nbsp;</U></STRONG></P><pre class="lia-code-sample language-abap"><code>METHOD fields. SELECT <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/2449">@abap_true</a> INTO <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1407137">@DATA</a>(lv_valid) FROM zses_file_table UP TO 1 ROWS WHERE end_user = <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/4244">@SY</a>-uname. ENDSELECT. IF lv_valid &lt;&gt; abap_true. INSERT zses_file_table FROM @( VALUE #( end_user = sy-uname ) ). ENDIF. MODIFY ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file UPDATE FROM VALUE #( FOR key IN keys ( end_user = key-end_user status = ' ' " Accepted %control-status = if_abap_behv=&gt;mk-on ) ). IF keys[ 1 ]-%is_draft = '01'. MODIFY ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file EXECUTE uploadexceldata FROM CORRESPONDING #( keys ). ENDIF. ENDMETHOD.</code></pre><P>Explanation:<BR />1. We save an entry with our username to the parent table, so that the once an instance is created and it goes inside the draft, it finds an instance to edit.<BR />2. We call the action&nbsp;<SPAN><STRONG>uploadexceldata</STRONG> such that whenever a new file is uploaded, it converts the data and store it into the child table.</SPAN></P><P><U><STRONG>Action method 'uploadExcelData' -</STRONG></U></P><pre class="lia-code-sample language-abap"><code> READ ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_inv). DATA(lv_attachment) = lt_inv[ 1 ]-attachment. DATA: rows TYPE STANDARD TABLE OF string, content TYPE string, conv TYPE REF TO cl_abap_conv_in_ce, ls_excel_data TYPE zses_db, lt_excel_data TYPE STANDARD TABLE OF zses_db, lv_quantity TYPE char10, lv_entrysheet TYPE ebeln. conv = cl_abap_conv_in_ce=&gt;create( input = lv_attachment ). conv-&gt;read( IMPORTING data = content ). SPLIT content AT cl_abap_char_utilities=&gt;cr_lf INTO TABLE rows. LOOP AT rows INTO DATA(ls_row). SPLIT ls_row AT ',' INTO ls_excel_data-entrysheet ls_excel_data-ebeln ls_excel_data-ebelp ls_excel_data-ext_number ls_excel_data-begdate ls_excel_data-enddate lv_quantity "ls_attdata-BASE_UOM ls_excel_data-fin_entry. ls_excel_data-entrysheet = lv_entrysheet = |{ ls_excel_data-entrysheet ALPHA = IN }|. ls_excel_data-ebeln = |{ ls_excel_data-ebeln ALPHA = IN }|. ls_excel_data-ebelp = |{ ls_excel_data-ebelp ALPHA = IN }|. ls_excel_data-quantity = CONV #( lv_quantity ). APPEND ls_excel_data TO lt_excel_data. CLEAR: ls_row, ls_excel_data. ENDLOOP.</code></pre><P>Explanation -<BR />1. Read the excel file (CSV in our case) from the entity and append it to internal table LT_EXCEL_DATA.<BR />2. Use EML to insert the data from LT_EXCEL_DATA to the child table.&nbsp;<BR /><BR />Please refer below code for the implementation class -</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>CLASS lhc_exceldata DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR exceldata RESULT result. METHODS createses FOR MODIFY IMPORTING keys FOR ACTION exceldata~createses RESULT result. ENDCLASS. CLASS lhc_exceldata IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD createses. READ ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY exceldata ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_data). DATA: ls_header_data TYPE i_serviceentrysheettp_2, ls_item_data TYPE i_serviceentrysheetitemtp_2, lt_header_crt TYPE TABLE FOR CREATE i_serviceentrysheettp_2\\serviceentrysheet, lt_item_cba TYPE TABLE FOR CREATE i_serviceentrysheettp_2\\serviceentrysheet\_serviceentrysheetitem. * Create SES with reference to a Service Purchase Order Item *--- Prepare Header Data ls_header_data-serviceentrysheetname = 'Demo SES'. ls_header_data-purchaseorder = `4500000286`. ls_header_data-sesoriginobjecttype = `EX`. *--- Prepare Item Data ls_item_data-purchaseorderitem = '00010'. ls_item_data-confirmedquantity = '1'. ls_item_data-accountassignmentcategory = 'K'. ls_item_data-serviceperformancedate = '20230207'. ls_item_data-multipleacctassgmtdistribution = '0'. *--- Prepare Payload APPEND INITIAL LINE TO lt_header_crt ASSIGNING FIELD-SYMBOL(&lt;ls_hdr_crt&gt;). &lt;ls_hdr_crt&gt; = CORRESPONDING #( ls_header_data CHANGING CONTROL ). &lt;ls_hdr_crt&gt;-%cid = `HEADER_1` . APPEND INITIAL LINE TO lt_item_cba ASSIGNING FIELD-SYMBOL(&lt;ls_itm_cba&gt;). &lt;ls_itm_cba&gt;-%cid_ref = 'HEADER_1'. APPEND INITIAL LINE TO &lt;ls_itm_cba&gt;-%target ASSIGNING FIELD-SYMBOL(&lt;item_data&gt;). &lt;item_data&gt; = CORRESPONDING #( ls_item_data CHANGING CONTROL ). &lt;item_data&gt;-%cid = 'Item_1'. MODIFY ENTITIES OF i_serviceentrysheettp_2 ENTITY serviceentrysheet CREATE FROM lt_header_crt CREATE BY \_serviceentrysheetitem FROM lt_item_cba FAILED DATA(ls_failed_crt) REPORTED DATA(ls_reported_crt) MAPPED DATA(ls_mapped_crt). ENDMETHOD. ENDCLASS. CLASS lhc_file DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR file RESULT result. METHODS uploadexceldata FOR MODIFY IMPORTING keys FOR ACTION file~uploadexceldata RESULT result. METHODS fields FOR DETERMINE ON MODIFY IMPORTING keys FOR file~fields. METHODS get_instance_features FOR INSTANCE FEATURES IMPORTING keys REQUEST requested_features FOR file RESULT result. ENDCLASS. CLASS lhc_file IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD uploadexceldata. ** Check if there exist an entry with current logged in username in parent table SELECT <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/2449">@abap_true</a> INTO <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1407137">@DATA</a>(lv_valid) FROM zses_file_table UP TO 1 ROWS WHERE end_user = <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/4244">@SY</a>-uname. ENDSELECT. ** Create one entry, if it does not exist IF lv_valid &lt;&gt; abap_true. INSERT zses_file_table FROM @( VALUE #( end_user = sy-uname ) ). ENDIF. ** Read the parent instance READ ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_inv). ** Get attachment value from the instance DATA(lv_attachment) = lt_inv[ 1 ]-attachment. ** Data declarations DATA: rows TYPE STANDARD TABLE OF string, content TYPE string, conv TYPE REF TO cl_abap_conv_in_ce, ls_excel_data TYPE zses_db, lt_excel_data TYPE STANDARD TABLE OF zses_db, lv_quantity TYPE char10, lv_entrysheet TYPE ebeln. ** Convert excel file with CSV format into internal table of type string conv = cl_abap_conv_in_ce=&gt;create( input = lv_attachment ). conv-&gt;read( IMPORTING data = content ). ** Split the string table to rows SPLIT content AT cl_abap_char_utilities=&gt;cr_lf INTO TABLE rows. ** Process the rows and append to the internal table LOOP AT rows INTO DATA(ls_row). SPLIT ls_row AT ',' INTO ls_excel_data-entrysheet ls_excel_data-ebeln ls_excel_data-ebelp ls_excel_data-ext_number ls_excel_data-begdate ls_excel_data-enddate lv_quantity "ls_attdata-BASE_UOM ls_excel_data-fin_entry. ls_excel_data-entrysheet = lv_entrysheet = |{ ls_excel_data-entrysheet ALPHA = IN }|. ls_excel_data-ebeln = |{ ls_excel_data-ebeln ALPHA = IN }|. ls_excel_data-ebelp = |{ ls_excel_data-ebelp ALPHA = IN }|. ls_excel_data-quantity = CONV #( lv_quantity ). APPEND ls_excel_data TO lt_excel_data. CLEAR: ls_row, ls_excel_data. ENDLOOP. ** Delete duplicate records DELETE ADJACENT DUPLICATES FROM lt_excel_data. DELETE lt_excel_data WHERE ebeln IS INITIAL. ** Prepare the datatypes to store the data from internal table lt_excel_data to child entity through EML DATA lt_att_create TYPE TABLE FOR CREATE zi_ses_parent\_ses_excel. lt_att_create = VALUE #( ( %cid_ref = keys[ 1 ]-%cid_ref %is_draft = keys[ 1 ]-%is_draft end_user = keys[ 1 ]-end_user %target = VALUE #( FOR ls_data IN lt_excel_data ( %cid = |{ ls_data-ebeln }{ ls_data-ebelp }| %is_draft = keys[ 1 ]-%is_draft end_user = sy-uname entrysheet = ls_data-entrysheet ebeln = ls_data-ebeln ebelp = ls_data-ebelp ext_number = ls_data-ext_number begdate = ls_data-begdate enddate = ls_data-enddate quantity = ls_data-quantity " BASE_UOM = ls_data- fin_entry = ls_data-fin_entry %control = VALUE #( end_user = if_abap_behv=&gt;mk-on entrysheet = if_abap_behv=&gt;mk-on ebeln = if_abap_behv=&gt;mk-on ebelp = if_abap_behv=&gt;mk-on ext_number = if_abap_behv=&gt;mk-on begdate = if_abap_behv=&gt;mk-on enddate = if_abap_behv=&gt;mk-on quantity = if_abap_behv=&gt;mk-on " BASE_UOM = ls_data- fin_entry = if_abap_behv=&gt;mk-on ) ) ) ) ). READ ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file BY \_ses_excel ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_excel). ** Delete already existing entries from child entity MODIFY ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY exceldata DELETE FROM VALUE #( FOR ls_excel IN lt_excel ( %is_draft = ls_excel-%is_draft %key = ls_excel-%key ) ) MAPPED DATA(lt_mapped_delete) REPORTED DATA(lt_reported_delete) FAILED DATA(lt_failed_delete). ** Create the records from the new attached CSV file MODIFY ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file CREATE BY \_ses_excel AUTO FILL CID WITH lt_att_create. APPEND VALUE #( %tky = lt_inv[ 1 ]-%tky ) TO mapped-file. APPEND VALUE #( %tky = lt_inv[ 1 ]-%tky %msg = new_message_with_text( severity = if_abap_behv_message=&gt;severity-success text = 'Excel Data Uploaded' ) ) TO reported-file. MODIFY ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file UPDATE FROM VALUE #( ( %is_draft = keys[ 1 ]-%is_draft end_user = sy-uname status = 'P' " %data = VALUE #( status = 'P' ) %control = VALUE #( status = if_abap_behv=&gt;mk-on ) ) ) MAPPED DATA(lt_mapped_update) REPORTED DATA(lt_reported_update) FAILED DATA(lt_failed_update). READ ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_file_status). MODIFY ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file UPDATE FROM VALUE #( FOR ls_file_status IN lt_file_status ( %is_draft = ls_file_status-%is_draft %tky = ls_file_status-%tky %data = VALUE #( status = 'C' ) %control = VALUE #( status = if_abap_behv=&gt;mk-on ) ) ). READ ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(lt_file). result = VALUE #( FOR ls_file IN lt_file ( %tky = ls_file-%tky %param = ls_file ) ). ENDMETHOD. METHOD fields. SELECT <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/2449">@abap_true</a> INTO <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/1407137">@DATA</a>(lv_valid) FROM zses_file_table UP TO 1 ROWS WHERE end_user = <a href="https://community.sap.com/t5/user/viewprofilepage/user-id/4244">@SY</a>-uname. ENDSELECT. IF lv_valid &lt;&gt; abap_true. INSERT zses_file_table FROM @( VALUE #( end_user = sy-uname ) ). ENDIF. MODIFY ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file UPDATE FROM VALUE #( FOR key IN keys ( end_user = key-end_user status = ' ' " Accepted %control-status = if_abap_behv=&gt;mk-on ) ). IF keys[ 1 ]-%is_draft = '01'. MODIFY ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file EXECUTE uploadexceldata FROM CORRESPONDING #( keys ). ENDIF. ENDMETHOD. METHOD get_instance_features. READ ENTITIES OF zi_ses_parent IN LOCAL MODE ENTITY file FIELDS ( end_user ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_file). result = VALUE #( FOR ls_file IN lt_file ( %key = ls_file-%key %is_draft = ls_file-%is_draft %features-%action-uploadexceldata = COND #( WHEN ls_file-%is_draft = '00' THEN if_abap_behv=&gt;fc-f-read_only ELSE if_abap_behv=&gt;fc-f-unrestricted ) ) ). ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P>After this, create service definition, expose both the parent and child consumption views, create a service binding and see the wonders.<BR />The list report page would look like -&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Amit_Sharma_0-1712825902783.png" style="width: 653px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/95000iA5FC1C16D999EE71/image-dimensions/653x178?v=v2" width="653" height="178" role="button" title="Amit_Sharma_0-1712825902783.png" alt="Amit_Sharma_0-1712825902783.png" /></span></P><P>Since, we used where clause to filter the parent records based on the current logged-in user in the interface view ZI_SES_PARENT, we should see only 1 record here. You can also skip the list report page in SAP BAS and deploy the app which directly opens the object page.&nbsp;<BR />When navigate to the object page and click on edit.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Amit_Sharma_1-1712826078742.png" style="width: 675px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/95001i854176227A912408/image-dimensions/675x324?v=v2" width="675" height="324" role="button" title="Amit_Sharma_1-1712826078742.png" alt="Amit_Sharma_1-1712826078742.png" /></span></P><P>Once saved, we should see the records -</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Amit_Sharma_2-1712826242815.png" style="width: 673px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/95002i217919ACED4CC839/image-dimensions/673x330?v=v2" width="673" height="330" role="button" title="Amit_Sharma_2-1712826242815.png" alt="Amit_Sharma_2-1712826242815.png" /></span></P><P>You can write the logic to process these records in an instance action for child entity, just like we have an action defined as 'Process SES'.<BR />With this process, you can upload CSV file and process the records. You can try changing the logic to process XSLX file as well.&nbsp;<BR /><BR /></P> 2024-04-16T12:50:21.211000+02:00 https://community.sap.com/t5/application-development-blog-posts/writing-unveiling-abap-unit-tests-for-table-functions/ba-p/13669290 Writing/Unveiling ABAP Unit Tests for Table Functions 2024-04-16T13:01:57.878000+02:00 kishore_kumar_g https://community.sap.com/t5/user/viewprofilepage/user-id/1438038 <P style=" text-align : left; "><STRONG>Introduction</STRONG></P><P><EM>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; In SAP ABAP development, writing robust code is essential for ensuring the stability and reliability of applications. One powerful tool for achieving this is the ABAP Unit, which allows developers to automate the testing process and validate the behavior of their code. In this blog post, we will explore how to write ABAP unit tests specifically for table functions.</EM></P><P><STRONG><EM>ABAP CDS - Table Functions</EM></STRONG></P><P><EM>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;A CDS table function is defined in the&nbsp;DDL source code&nbsp;of a&nbsp;CDS data definition&nbsp;in the&nbsp;ABAP development tools for Eclipse&nbsp;using the statement&nbsp;<STRONG>DEFINE TABLE FUNCTION</STRONG>&nbsp;in the&nbsp;CDS DDL&nbsp;of the&nbsp;ABAP Core Data Services (CDS). A CDS table function includes the following (including an example that is included later in the blog):</EM></P><UL><LI><EM>The&nbsp;CDS entity (e.g., ZFLIGHTS )</EM></LI><LI><EM>An&nbsp;AMDP function implementation ( e.g.,&nbsp;<SPAN>ZCL_FLIGHT_DETAILS )</SPAN></EM></LI></UL><P><EM><SPAN>Here, we'll use a classic example of SFLIGHT data to illustrate the creation of the query within the table function.</SPAN></EM></P><P>- Table Function CDS Entity:</P><P style=" padding-left : 30px; "><SPAN>The following is the table function, equipped with a parameter to facilitate filtering during selection.</SPAN></P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Flights and its Details' @ClientHandling.algorithm: #SESSION_VARIABLE @ClientHandling.type: #CLIENT_DEPENDENT define table function ZFLIGHTS with parameters @Environment.systemField: #CLIENT P_mandt : abap.clnt, P_CarrierId : s_carr_id returns { key mandt : abap.clnt; key carrierId : s_carr_id; key connectionId : s_conn_id; key flightDate : s_date; name : s_carrname; planeType : s_planetye; seatsMax : s_seatsmax; currencyCode : s_currcode; } implemented by method ZCL_FLIGHT_DETAILS=&gt;get_details;</code></pre><P>&nbsp;</P><P><EM>- Implementation of the table AMDP function is&nbsp; below:</EM></P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>CLASS zcl_flight_details DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_amdp_marker_hdb. CLASS-METHODS get_details FOR TABLE FUNCTION zflights. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_flight_details IMPLEMENTATION. METHOD get_details BY DATABASE FUNCTION FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY USING s_flights s_carrier. RETURN select _flight.mandt, _flight.CarrierId, _flight.ConnectionId, _flight.FlightDate, _carrier.Name, _flight.planetype, _flight.seatsmax, _flight.currencyCode from s_flights as _flight left outer join s_carrier as _carrier on _flight.mandt = _carrier.mandt and _flight.CarrierId = _carrier.CarrierId where _flight.mandt = :P_mandt and _flight.CarrierId = :P_CarrierId; ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P>&nbsp;</P><P><EM><STRONG>Setting Up an ABAP Unit Environment</STRONG></EM></P><P><EM>&nbsp; &nbsp; &nbsp; &nbsp;Before crafting tests for table functions, it is imperative to establish the ABAP unit environment. This process entails crafting a test class along with methods to assess the functionality of the table function. Let's initiate the creation of a class named <FONT color="#FF9900">ZCL_AUNIT_ZFLIGHTS</FONT>, within which we'll create a local test class termed <FONT color="#FF9900">LCL_CL_AUNIT_ZFLIGHTS</FONT><STRONG><FONT color="#FF9900"><FONT color="#000000">.</FONT></FONT></STRONG></EM></P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>CLASS lcl_cl_aunit_zflights DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. PUBLIC SECTION. CLASS-DATA: environment TYPE REF TO if_amdp_test_environment. PRIVATE SECTION. CLASS-METHODS class_setup. CLASS-METHODS class_teardown. METHODS setup. ENDCLASS. CLASS lcl_cl_aunit_zflights IMPLEMENTATION. METHOD class_setup. " For testing an AMDP table function which is a CDS table function source, " 1. One would need to configure the AMDP table function class and the method name in the AMDP test configuration step. " Here the AMDP method 'ZCL_FLIGHT_DETAILS=&gt;GET_DETAILS' is a table function which is used as a source for the CDS table function 'CDSFRWK_TF_FLIGHT_BOOKING'. DATA(environment_config) = cl_amdp_test_environment=&gt;create_test_configuration( ). environment_config-&gt;add_amdp_class( 'ZCL_FLIGHT_DETAILS' )-&gt;add_methods_for_unit_test( VALUE #( ( 'GET_DETAILS' ) ) ). environment = cl_amdp_test_environment=&gt;create( environment_config ). ENDMETHOD. METHOD class_teardown. IF environment IS NOT INITIAL. environment-&gt;destroy( ). ENDIF. ENDMETHOD. METHOD setup. " Clears any existing test configuration on the test doubles which are set by other unit test methods. environment-&gt;clear_doubles( ). ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P><EM>In the method class_setup, the environment configuration is done, in which the name of the AMDP class and its method to be tested need to be given.</EM></P><P>&nbsp;</P><P><EM><STRONG>Mocking Data</STRONG></EM></P><P><EM>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;For mocking data, we'll create a new method named test_cds_table_function within the private section of the test class. This method will facilitate the generation of mock data for testing purposes.</EM></P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>PRIVATE SECTION. METHODS test_cds_table_function FOR TESTING RAISING cx_static_check.</code></pre><P>&nbsp;</P><P><EM>By default, if no client is specified, the system client will be assumed. Since I aim to demonstrate data mocking at the client level, I'll simulate data for two distinct clients and execute the query with client-specific information.</EM></P><P><EM>&nbsp; - Procedure to mock the data:</EM></P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>"To get the object instance of the dependency used in the AMDP Implementation Class DATA(lo_object) = environment-&gt;get_test_double( DEPENDENCY ). "Set the client for which the data is being mocked lo_object-&gt;get_view_content_config( )-&gt;for_client( CLIENT-INFO ). "Set the content of the dependency lo_object-&gt;get_view_content_config( )-&gt;set_content( CONTENT ).</code></pre><P>&nbsp;</P><P><EM>The actual Implementation of the method is below:</EM></P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code> METHOD test_cds_table_function. DATA: lt_flights_001 TYPE STANDARD TABLE OF s_flights, lt_flights_714 TYPE STANDARD TABLE OF s_flights, lt_carrier_001 TYPE STANDARD TABLE OF s_carrier, lt_carrier_714 type standard table of s_carrier. lt_flights_001 = VALUE #( ( CarrierId = 'AA' ConnectionId = '0101' FlightDate = '20240101' planetype = 'A340-600' seatsmax = '330' currencycode = 'USD' ) ( CarrierId = 'DL' ConnectionId = '0102' FlightDate = '20240201' planetype = 'A340-600' seatsmax = '330' currencycode = 'USD' ) ( CarrierId = 'DL' ConnectionId = '0103' FlightDate = '20240301' planetype = 'A340-600' seatsmax = '330' currencycode = 'USD' ) ). lt_carrier_001 = VALUE #( ( carrierid = 'AA' Name = 'American Airlines' ) ( carrierid = 'DL' Name = 'Delta Airlines' ) ). lt_flights_714 = VALUE #( ( CarrierId = 'AA' ConnectionId = '0104' FlightDate = '20240401' planetype = 'A340-600' seatsmax = '330' currencycode = 'USD' ) ( CarrierId = 'DL' ConnectionId = '0105' FlightDate = '20240501' planetype = 'A340-600' seatsmax = '330' currencycode = 'USD' ) ( CarrierId = 'DL' ConnectionId = '0106' FlightDate = '20240601' planetype = 'A340-600' seatsmax = '330' currencycode = 'USD' ) ). lt_carrier_714 = VALUE #( ( carrierid = 'AA' Name = 'American Airlines' ) ( carrierid = 'DL' Name = 'Delta Airlines' ) ). " Get the test double of "S_FLIGHTS" &amp; "S_CARRIER" from the test environment. DATA(lo_flight) = environment-&gt;get_test_double( 'S_FLIGHTS' ). DATA(lo_carrier) = environment-&gt;get_test_double( 'S_CARRIER' ). lo_flight-&gt;get_view_content_config( )-&gt;for_client( '001' ). lo_carrier-&gt;get_view_content_config( )-&gt;for_client( '001' ). lo_flight-&gt;get_view_content_config( )-&gt;set_content( lt_flights_001 ). lo_carrier-&gt;get_view_content_config( )-&gt;set_content( lt_carrier_001 ). lo_flight-&gt;get_view_content_config( )-&gt;for_client( '714' ). lo_carrier-&gt;get_view_content_config( )-&gt;for_client( '714' ). lo_flight-&gt;get_view_content_config( )-&gt;set_content( lt_flights_714 ). lo_carrier-&gt;get_view_content_config( )-&gt;set_content( lt_carrier_714 ). SELECT * FROM zflights( p_carrierid = 'DL' ) USING CLIENT '714' INTO TABLE (lt_result). cl_abap_unit_assert=&gt;assert_equals( EXPORTING act = lines( lt_result ) " Data object with current value exp = 2 " Data object with expected type ). ENDMETHOD.</code></pre><P>&nbsp;</P><P>&nbsp;</P><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><DIV class=""><P>&nbsp;</P><P><EM>While executing the query for client '714', <FONT color="#FF9900">LT_RESULT</FONT> acquired the following records. The assertion verifies the successful retrieval of 2 records for client '714'.</EM></P><P>&nbsp;</P><P>&nbsp;</P></DIV></DIV></DIV><DIV class=""><DIV class=""><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Output.jpg" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/96052i8B7038EB56273B35/image-size/medium?v=v2&amp;px=400" role="button" title="Output.jpg" alt="Output.jpg" /></span></DIV><DIV class="">&nbsp;</DIV><DIV class="">&nbsp;</DIV><DIV class="">&nbsp;</DIV><DIV class="">&nbsp;</DIV><DIV class="">&nbsp;</DIV><DIV class="">&nbsp;</DIV><DIV class="">&nbsp;</DIV><DIV class="">&nbsp;</DIV><DIV class="">&nbsp;</DIV><DIV class=""><EM><STRONG>Conclusion</STRONG></EM></DIV><DIV class=""><EM>Writing ABAP unit tests holds significant importance in SAP ABAP development.&nbsp;Thorough testing not only confirms the accuracy of table functions but also enhances the overall stability and performance of the system.&nbsp;</EM></DIV><DIV class="">&nbsp;</DIV><DIV class=""><EM>Happy Coding&nbsp;<span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></EM></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV></DIV> 2024-04-16T13:01:57.878000+02:00 https://community.sap.com/t5/application-development-blog-posts/consume-restful-webservices-using-post-man/ba-p/13674961 Consume Restful Webservices using POST MAN 2024-04-19T15:56:31.284000+02:00 Narasimha_Sesti https://community.sap.com/t5/user/viewprofilepage/user-id/169013 <P>HTTP-based APIs integrate easily with RESTful web services. There are many ways to use HTTP methods to consume and update and retrieve&nbsp; these API data.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_5-1713445164735.jpeg" style="width: 346px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98181i35EFB49511605861/image-dimensions/346x329?v=v2" width="346" height="329" role="button" title="Narasimha_Sesti_5-1713445164735.jpeg" alt="Narasimha_Sesti_5-1713445164735.jpeg" /></span></P><P>HTTP API methods and understand how to use them appropriately on resources</P><P><STRONG>Method 1: GET</STRONG></P><P>The most common HTTP method is GET, which returns a representational view of a resource's contents and data. GET should be used in read-only mode, which keeps the data safe, You should get the same results no matter how many times you use this method, unless it is modified by another client in the interim.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_16-1713446097628.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98210iC3CD233A5F51F22F/image-size/large?v=v2&amp;px=999" role="button" title="Narasimha_Sesti_16-1713446097628.png" alt="Narasimha_Sesti_16-1713446097628.png" /></span></P><P><STRONG>Method 2: POST</STRONG></P><P>Post request used to create resource (Record ) in server, While creating record we have to pass Body data Parameters(key, values) to create the Record as JSON, <STRONG>Form</STRONG> formats are supported.</P><P>&nbsp;</P><P>&nbsp;<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_17-1713446242193.png" style="width: 744px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98228iF60BE263AACC5C88/image-dimensions/744x374?v=v2" width="744" height="374" role="button" title="Narasimha_Sesti_17-1713446242193.png" alt="Narasimha_Sesti_17-1713446242193.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_26-1713448773787.png" style="width: 665px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98282iB57D135E93A5883A/image-dimensions/665x336?v=v2" width="665" height="336" role="button" title="Narasimha_Sesti_26-1713448773787.png" alt="Narasimha_Sesti_26-1713448773787.png" /></span></P><P>While submitting the data we have to update <STRONG>Header&nbsp;</STRONG>tab <STRONG>content type</STRONG> with <STRONG>application/JSON</STRONG>.</P><P>After create new record by using browser you&nbsp; will received updated data. By default Browsers using HTTP Protocol with GET method to retrieve data from service.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_18-1713446586084.png" style="width: 672px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98245iF7E7E1C61155DBA5/image-dimensions/672x351?v=v2" width="672" height="351" role="button" title="Narasimha_Sesti_18-1713446586084.png" alt="Narasimha_Sesti_18-1713446586084.png" /></span></P><P><STRONG>Method 3: PATCH</STRONG></P><P>PATCH is another HTTP method used to update resources. As opposed to replacing resources, like the PUT method does, PATCH only modifies resource contents. As a general rule, these modifications worked based on Key.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_19-1713446858588.png" style="width: 728px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98248iB8B97E1D86EBA46E/image-dimensions/728x383?v=v2" width="728" height="383" role="button" title="Narasimha_Sesti_19-1713446858588.png" alt="Narasimha_Sesti_19-1713446858588.png" /></span></P><P>While submitting the data we have to update <STRONG>Header&nbsp;</STRONG>tab <STRONG>content type</STRONG> with <STRONG>application/JSON</STRONG>.</P><P>Patch update the record based on Id, after update you will find latest records by refresh your browser.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_21-1713447375169.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98257iEB9232BD3C57A48C/image-size/medium?v=v2&amp;px=400" role="button" title="Narasimha_Sesti_21-1713447375169.png" alt="Narasimha_Sesti_21-1713447375169.png" /></span></P><P>We have create additional record 4 for other operations in above list.</P><P><STRONG>Method 5: DELETE</STRONG></P><P>The last HTTP method DELETE. When a DELETE method targets a single resource, that resource is removed entirely(Hard Delete). we have to pass based on Key ID (4).</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_22-1713447488529.png" style="width: 721px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98259i6B207E0BC8840A82/image-dimensions/721x376?v=v2" width="721" height="376" role="button" title="Narasimha_Sesti_22-1713447488529.png" alt="Narasimha_Sesti_22-1713447488529.png" /></span></P><P>Delete Method will delete the 4th record based on Key. after we can refresh browser data.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_23-1713447561137.png" style="width: 660px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98260i7AF2219A1C58061E/image-dimensions/660x416?v=v2" width="660" height="416" role="button" title="Narasimha_Sesti_23-1713447561137.png" alt="Narasimha_Sesti_23-1713447561137.png" /></span></P><P><STRONG>Authentication</STRONG></P><P>Authentication tab help to protect the API data using different security ways.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_24-1713447959184.png" style="width: 551px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98265iE8DB8E370885E2B5/image-dimensions/551x354?v=v2" width="551" height="354" role="button" title="Narasimha_Sesti_24-1713447959184.png" alt="Narasimha_Sesti_24-1713447959184.png" /></span></P><P>&nbsp;</P><H2 id="toc-hId-992590019">Basic Authentication</H2><P><STRONG>Basic Authentication is a method of securing HTTP requests through a special header</STRONG>:</P><H2 id="toc-hId-796076514">Authorization Tab</H2><P>let’s send a GET request to a Basic Auth-secured endpoint and expect an&nbsp;<EM>Unauthorized</EM>&nbsp;status for the response:</P><P>Now, let’s add the credentials. To do this,&nbsp;<STRONG>we simply go to the “</STRONG><EM><STRONG>Authorization</STRONG></EM><STRONG>” tab and select “</STRONG><EM><STRONG>Basic Auth</STRONG></EM><STRONG>” as the authorization type</STRONG>. After that, we insert the username and password and we’re all set:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_25-1713448188952.png" style="width: 726px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98274i9D0F299688A06FC9/image-dimensions/726x212?v=v2" width="726" height="212" role="button" title="Narasimha_Sesti_25-1713448188952.png" alt="Narasimha_Sesti_25-1713448188952.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P><SPAN><STRONG>2) Authorization</STRONG></SPAN><SPAN>: </SPAN>Basic &lt;credentials&gt;</P><P>To generate the credentials token, we need to write the username and password, joined by the semicolon character. After that, we need to encode the resulting string with Base64.</P><P style=" text-align : justify; ">Let’s assume the username is “<EM>admin</EM>” and the password is “<EM>baeldung</EM>“. First, we’ll create the credentials string, which will be “<EM>admin:baeldung</EM>“. Then, we’ll encode it with Base64, add the “<EM>Basic</EM>” keyword, and set it as the <STRONG>header’s</STRONG> value:</P><H2 id="toc-hId-599563009">&nbsp;</H2><H3 id="toc-hId-532132223"><STRONG>What is Bearer Authentication?</STRONG></H3><P style=" text-align : justify; ">HTTP provides a framework for controlling access to protected resources. HTTP authentication&nbsp;is performed by sending authentication credentials in the authorization header to access the protected resource. Bearer authentication (also called token authentication) is one of the HTTP authentication schemes that grant access to the bearer of this token. Bearer token authentication is done by sending a security token with every HTTP request we make to the server. You can do bearer authentication with any programming language.</P><P><STRONG>Bearer Token Authentication Syntax</STRONG></P><P>Authorization: Bearer {token}</P><H1 id="toc-hId-77453280"><SPAN>&nbsp;Postman displays the response data sent from the server in the lower pane.</SPAN></H1><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Narasimha_Sesti_4-1713445164731.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/98183i2C05037925C601A8/image-size/medium?v=v2&amp;px=400" role="button" title="Narasimha_Sesti_4-1713445164731.png" alt="Narasimha_Sesti_4-1713445164731.png" /></span></P> 2024-04-19T15:56:31.284000+02:00 https://community.sap.com/t5/technology-blogs-by-sap/exploring-graphql-and-rest-for-sap-developers/ba-p/13681247 Exploring GraphQL and REST for SAP Developers 2024-04-26T14:10:06.068000+02:00 Marwah_Shwaiki https://community.sap.com/t5/user/viewprofilepage/user-id/7391 <P>I came across GraphQL while working on a side project and as a SAP Developer who worked a lot with REST APIs, I was wondering what advantages GraphQL could offer me, and how to apply it for different use cases. So, I decided to explore both APIs and how to use them in the SAP context. Here is a brief overview of my thoughts about both APIs.</P><P><STRONG>Introduction</STRONG></P><P>let's start with a brief definition of the APIs concept, APIs are the lifeblood of modern software development, bridging the gap between different systems. They define rules for accessing functionality and data remotely. Let's delve into API concepts and see how GraphQL and REST fit into the SAP world.</P><P>Over the past decade, REST has been the go-to for web APIs, but it's not without its flaws. Enter GraphQL, developed to tackle the shortcomings of REST and provide more flexibility and efficiency for developers.</P><P><STRONG>Understanding APIs:</STRONG> APIs act as intermediaries facilitating communication between software applications. They come in two main flavors: SOAP, known for its strict standards, and REST, favored for its simplicity and scalability.</P><P><STRONG>GraphQL:</STRONG> GraphQL, developed by Facebook, offers a more powerful alternative to REST. It allows clients to request specific data, reducing over-fetching issues common in REST APIs.</P><P><STRONG>REST:</STRONG> REST, an architectural style for building networked applications, relies on stateless, client-server communication via HTTP.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="6d0omhgylpa6e584fqot.jpg" style="width: 566px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/101281i6D79EE1E4F1D1BD7/image-size/large?v=v2&amp;px=999" role="button" title="6d0omhgylpa6e584fqot.jpg" alt="6d0omhgylpa6e584fqot.jpg" /></span></P><P>&nbsp;</P><H3 id="toc-hId-1122500103"><STRONG>Key Differences</STRONG></H3><OL><LI><STRONG>Data Fetching:</STRONG> GraphQL enables precise data fetching with a single request, unlike REST which may lead to over-fetching or under-fetching.</LI><LI><STRONG>Endpoint Complexity:</STRONG> REST APIs often require multiple endpoints, while GraphQL offers a single endpoint handling various data requests.</LI><LI><STRONG>Versioning:</STRONG> REST commonly relies on versioning, unlike GraphQL which allows backward-compatible changes without versioning.</LI><LI><STRONG>Caching:</STRONG> REST APIs leverage HTTP caching mechanisms, while GraphQL responses typically require additional configuration for caching.</LI></OL><H2 id="toc-hId-796903879"><STRONG>Similarities</STRONG></H2><P>Both GraphQL and REST facilitate data exchange between services or applications, following a client-server model.</P><H3 id="toc-hId-729473093"><STRONG>Architecture</STRONG></H3><UL><LI>Both are stateless and use a client-server model based on HTTP.</LI></UL><H3 id="toc-hId-532959588"><STRONG>Resource-based Design</STRONG></H3><P>Both REST and GraphQL design data interchange around resources, each with its unique identifier and set of operations.</P><H3 id="toc-hId-336446083"><STRONG>Data Exchange</STRONG></H3><P>Both support JSON as the primary data exchange format and caching for improved performance.</P><H2 id="toc-hId-10849859"><STRONG>When to Use What?</STRONG></H2><P><STRONG>Use REST When:</STRONG></P><UL><LI>You need a straightforward and widely adopted solution.</LI><LI>Your data requirements are consistent and well-defined.</LI><LI>Caching is crucial for your application's performance.</LI><LI>You're working with legacy systems that are compatible with REST.</LI></UL><P><STRONG>Use GraphQL When:</STRONG></P><UL><LI>Your data requirements are dynamic and vary across different clients.</LI><LI>Over-fetching or under-fetching of data is a concern.</LI><LI>You want to reduce the number of API calls and improve efficiency.</LI><LI>You're developing a new application or service and want flexibility in data retrieval.</LI></UL><H3 id="toc-hId--56580927"><STRONG>Conclusion</STRONG></H3><P>GraphQL and REST are vital for integration and data exchange in the SAP ecosystem. While REST is popular for its simplicity, GraphQL's flexibility is gaining traction. Understanding their nuances empowers SAP developers to design effective APIs for their solutions.</P><P><!-- notionvc: 029c7c77-dc17-463a-acc4-5b13f32d69f4 --></P> 2024-04-26T14:10:06.068000+02:00 https://community.sap.com/t5/technology-blogs-by-members/adobe-form-with-rap-based-odata-service/ba-p/13700430 Adobe form with RAP Based OData Service 2024-05-14T10:53:12.611000+02:00 SachinArtani https://community.sap.com/t5/user/viewprofilepage/user-id/168423 <P>This blog emphasizes on the data source of custom developed Adobe form with fragments. But before we begin, below are the summarized instructions on the creation of a form Data Provider by SAP:</P><UL><LI><EM>In SAP S/4HANA Cloud, the creation of a gateway service via SEGW is only possible by SAP.</EM></LI><LI><EM>The concept is intended to be "cloud-ready" under all circumstances.</EM></LI><LI><EM>It is not intended for the customer to create a form data provider, neither from on-premise nor from cloud. Only SAP creates a form data provider.</EM></LI><LI><EM>If the customer still uses on-premise, but later switches to cloud, there is no effort required to adapt.</EM></LI><LI><EM>The customer can create their own forms but must use a form data provider from SAP.<BR />Typically, they will download a pre-delivered SAP template and create an adapted copy of it.</EM></LI><LI><EM>The customer’s form uses the same form data provider as the SAP template.</EM></LI></UL><P>For details, please refer: <A href="https://learning.sap.com/learning-journeys/getting-started-with-sap-forms-service-by-adobe/explaining-master-form-templates_cb9c491f-6647-4165-b7ad-f357c6007902" target="_blank" rel="noopener noreferrer">https://learning.sap.com/learning-journeys/getting-started-with-sap-forms-service-by-adobe/explaining-master-form-templates_cb9c491f-6647-4165-b7ad-f357c6007902</A></P><P>Keeping in mind the information above, if business demands for the custom solution, then we can proceed creating custom data provider service for custom adobe form with fragments. In this blog, we will demonstrate Adobe form with fragment with form type – ‘Content’ without master form template.</P><P>To achieve that, we will be following below steps –</P><OL><LI>Create an Adobe form</LI><LI>Create an OData service</LI><LI>Map OData service to adobe form</LI><LI>Design form layout</LI><LI>Create an OData service act as a driver program to call the Adobe form</LI><LI>Consume driver OData service in UI5</LI></OL><P>&nbsp;</P><P><STRONG><U>Create an Adobe form:</U></STRONG></P><P>Adobe forms with OData service can only be created through the Fiori app – ‘Maintain Form Templates’.</P><P>When we create an adobe form, we must specify a data source with OData service. But it does not list custom OData service by default. We also must upload a template while creating a form. For template, we can first create a layout without any bindings in Adobe LiveCycle Designer tool and save it as XDP file.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_0-1715673968502.png" style="width: 358px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109977i92F252850C87027B/image-dimensions/358x336?v=v2" width="358" height="336" role="button" title="SachinArtani_0-1715673968502.png" alt="SachinArtani_0-1715673968502.png" /></span></P><P>Alternatively, you can try to find a standard form which has similar attributes and copy it and create a custom adobe form. This will pick the template from the standard form which we can override with our changes.</P><P>&nbsp;</P><P><STRONG><U>Create an OData service:</U></STRONG></P><P>Based on the complexity of data, we will decide which OData service creation is best suited for the requirement:</P><UL><LI>DDIC Based SAP Gateway OData Service</LI><LI>OData Service with Data Source Reference as CDS Views</LI><LI>SAP RAP OData V2 Service</LI></UL><P>The prerequisite for opting any of the above option is that there will always be a root entity which must have all the key fields like the parent node. Didn’t get it?</P><P>Let us understand it better with an example of standard OData service - FDP_EF_PURCHASE_ORDER which is used for an Adobe form MM_PUR_PURCHASE_ORDER –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_1-1715673968506.png" style="width: 743px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109976iC2B07E56B62DF717/image-dimensions/743x151?v=v2" width="743" height="151" role="button" title="SachinArtani_1-1715673968506.png" alt="SachinArtani_1-1715673968506.png" /></span></P><P>&nbsp;</P><P>This OData service has ‘PurchaseOrder’ as the parent node and ‘PurchaseOrderItems’ as the child node. The association of one to many has also been maintained –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_2-1715673968509.png" style="width: 746px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109978iD59E9249E62CB8E2/image-dimensions/746x51?v=v2" width="746" height="51" role="button" title="SachinArtani_2-1715673968509.png" alt="SachinArtani_2-1715673968509.png" /></span></P><P>&nbsp;</P><P>Even though the parent node of the OData service is the Entity Set – ‘PurchaseOrder’. The entry point for this OData service is the Entity Set - ‘Query’, so we consider ‘Query’ as the root node.</P><P>For same reason, we have association and association sets are maintained between ‘QueryNode’ and ‘PurchaseOrderNode’ –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_3-1715673968511.png" style="width: 753px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109979i4EF4DAE0A365F109/image-dimensions/753x111?v=v2" width="753" height="111" role="button" title="SachinArtani_3-1715673968511.png" alt="SachinArtani_3-1715673968511.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_4-1715673968513.png" style="width: 752px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109981i05ED99029D10BB43/image-dimensions/752x173?v=v2" width="752" height="173" role="button" title="SachinArtani_4-1715673968513.png" alt="SachinArtani_4-1715673968513.png" /></span></P><P>In case of RAP based OData service as well, we need to maintain the relationship in similar fashion that there will be a root node with composition to the node which is the parent node. And that parent node will again have composition node if needed.</P><P>So, for purchase order header and item, we will create a root node including the keys of purchase order header. Then we will create a child of the root node which will be purchase order header. And finally, we will create purchase order item as a child of purchase order header.</P><P>As for the prerequisite, we can see that the parent node - ‘PurchaseOrderNode’ has a key ‘PurchaseOrder’ –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_5-1715673968514.png" style="width: 720px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109980i6429F6E5AB7B5DB1/image-dimensions/720x108?v=v2" width="720" height="108" role="button" title="SachinArtani_5-1715673968514.png" alt="SachinArtani_5-1715673968514.png" /></span></P><P>And the root node ‘QueryNode’ also has the key ‘PurchaseOrder’ along with few more keys –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_6-1715673968518.png" style="width: 719px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109984iC32EAC8CE5359F9E/image-dimensions/719x245?v=v2" width="719" height="245" role="button" title="SachinArtani_6-1715673968518.png" alt="SachinArtani_6-1715673968518.png" /></span></P><P>For developing a custom DDIC Based SAP Gateway OData service for adobe form, we can refer to any standard OData service such as FDP_EF_PURCHASE_ORDER from the Fiori app - ‘Maintain Form Templates’.</P><P>In this blog, we will demonstrate creating RAP based OData V2 service as a data source of adobe form. Note that SAP does not support OData V4 services for Adobe Forms developed through the Fiori app – ‘Maintain Form Templates’ as of today.</P><P>So, let’s dive to practical part and develop a RAP based OData V2 Web API. The steps are as follows:</P><P>As mentioned before, first we will create a root node ZSAC_R_PO_QUERY –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_7-1715673968521.png" style="width: 720px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109982iDFE1DE9421BC06BA/image-dimensions/720x161?v=v2" width="720" height="161" role="button" title="SachinArtani_7-1715673968521.png" alt="SachinArtani_7-1715673968521.png" /></span></P><P>Then comes the parent node ZSAC_I_PO_HEADER which is purchase order header –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_8-1715673968523.png" style="width: 723px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109983i040F9EA8F0A1E22D/image-dimensions/723x226?v=v2" width="723" height="226" role="button" title="SachinArtani_8-1715673968523.png" alt="SachinArtani_8-1715673968523.png" /></span></P><P>And then comes the child node ZSAC_I_PO_ITEM which is purchase order item –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_9-1715673968524.png" style="width: 720px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109986i314E727EBE16539C/image-dimensions/720x243?v=v2" width="720" height="243" role="button" title="SachinArtani_9-1715673968524.png" alt="SachinArtani_9-1715673968524.png" /></span></P><P>It’s quite simple, right? It is just a two-level CDS hierarchy where the root node has all the key fields present in parent node.</P><P>Now we just create service definition ZSAC_UI_PURCHASE_ORDER, expose all three data definitions we just created –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_10-1715673968526.png" style="width: 411px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109985i84BF48394E668F1B/image-dimensions/411x140?v=v2" width="411" height="140" role="button" title="SachinArtani_10-1715673968526.png" alt="SachinArtani_10-1715673968526.png" /></span></P><P>And finally, we create a service binding of type OData V2 Web API as ZSAC_UI_PURCHASE_ORDER –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_11-1715673968529.png" style="width: 766px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109987iB9FD6E8F6A0D5061/image-dimensions/766x323?v=v2" width="766" height="323" role="button" title="SachinArtani_11-1715673968529.png" alt="SachinArtani_11-1715673968529.png" /></span></P><P>For OData service using Data Source Reference as CDS Views, we can design the CDS nodes relation in the manner explained above.</P><P><STRONG><U>Map OData service to adobe form:</U></STRONG></P><P>Now that OData service is created, map it to the custom form by opening it in T-code: SFP –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_12-1715673968531.png" style="width: 585px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109989i67E72FEE0AE75BBB/image-dimensions/585x329?v=v2" width="585" height="329" role="button" title="SachinArtani_12-1715673968531.png" alt="SachinArtani_12-1715673968531.png" /></span></P><P>You can validate the change by checking for form in Fiori app – ‘Maintain Form Templates’ –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_13-1715673968533.png" style="width: 574px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109988iB653311422796766/image-dimensions/574x297?v=v2" width="574" height="297" role="button" title="SachinArtani_13-1715673968533.png" alt="SachinArtani_13-1715673968533.png" /></span></P><P><STRONG><U>Design form layout:</U></STRONG></P><P>Download the layout from the Fiori app –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_14-1715673968537.png" style="width: 623px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109990i85C14D27FFCA8850/image-dimensions/623x277?v=v2" width="623" height="277" role="button" title="SachinArtani_14-1715673968537.png" alt="SachinArtani_14-1715673968537.png" /></span></P><P>Open Adobe LiveCycle Designer app and open the XSD file. Click yes to the below popup which will load the bindings from the custom OData service we created –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_15-1715673968539.png" style="width: 395px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109991i130D0563E3031926/image-dimensions/395x201?v=v2" width="395" height="201" role="button" title="SachinArtani_15-1715673968539.png" alt="SachinArtani_15-1715673968539.png" /></span></P><P>We can find the bindings under ‘Data View’ tab as shown below –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_16-1715673968543.png" style="width: 750px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109993iC7F176BF049E2198/image-dimensions/750x281?v=v2" width="750" height="281" role="button" title="SachinArtani_16-1715673968543.png" alt="SachinArtani_16-1715673968543.png" /></span></P><P>Once you design the form as per requirement, save it, and upload it back –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_17-1715673968547.png" style="width: 630px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109994iCD791A84D12B3D4A/image-dimensions/630x271?v=v2" width="630" height="271" role="button" title="SachinArtani_17-1715673968547.png" alt="SachinArtani_17-1715673968547.png" /></span></P><P><STRONG><U>Create an OData service act as a driver program to call the Adobe form:</U></STRONG></P><P>In our case, we need an OData service which take Purchase Order as input and return the PDF with details in the designed layout. For that, we will create a Custom entity-based RAP Web API.</P><P>CDS View - ZSAC_I_PO_PDF</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_18-1715673968549.png" style="width: 636px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109995i08B62E24128F7187/image-dimensions/636x229?v=v2" width="636" height="229" role="button" title="SachinArtani_18-1715673968549.png" alt="SachinArtani_18-1715673968549.png" /></span></P><P>Below is the logic in the query class ZSAC_CL_PDF –</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>CLASS zsac_cl_pdf DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_rap_query_provider . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zsac_cl_pdf IMPLEMENTATION. METHOD if_rap_query_provider~select. DATA: lt_tab TYPE TABLE OF zsac_i_po_pdf, lv_content TYPE xstring, lo_cl_somu_form_services TYPE REF TO cl_somu_form_services, lt_keys TYPE cl_somu_form_services=&gt;ty_gt_key. TRY. IF io_request-&gt;is_data_requested( ). DATA(lv_offset) = io_request-&gt;get_paging( )-&gt;get_offset( ). DATA(lv_page_size) = io_request-&gt;get_paging( )-&gt;get_page_size( ). DATA(lv_max_rows) = COND #( WHEN lv_page_size = if_rap_query_paging=&gt;page_size_unlimited THEN 0 ELSE lv_page_size ) . TRY. DATA(lt_parameters) = io_request-&gt;get_parameters( ). DATA(lv_po) = VALUE #( lt_parameters[ parameter_name = 'P_PURCHASEORDER' ]-value OPTIONAL ). lt_keys = VALUE #( ( name = 'PurchaseOrder' value = lv_po ) ) . lo_cl_somu_form_services = cl_somu_form_services=&gt;get_instance( ). TRY. lo_cl_somu_form_services-&gt;get_document( EXPORTING iv_form_name = 'ZZ1_PO_FORM' it_key = lt_keys IMPORTING ev_content = lv_content ). CATCH cx_somu_error INTO DATA(lv_formerror). ENDTRY. lt_tab = VALUE #( ( pdf = lv_content ) ). io_response-&gt;set_total_number_of_records( 1 ). io_response-&gt;set_data( lt_tab ). CATCH cx_rap_query_filter_no_range INTO DATA(lv_range). DATA(lv_msg) = lv_range-&gt;get_text( ). ENDTRY. ENDIF. CATCH cx_rap_query_provider. ENDTRY. ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P>Logic explanation:</P><UL><LI>Taking the value from input parameter: P_PURCHASEORDER</LI><LI>Populating LT_KEYS with input parameters where we are expected to pass all the key fields mentioned in the root node ZSAC_R_PO_QUERY</LI><LI>Instantiate the class CL_SOMU_FORM_SERVICES</LI><LI>Call method GET_DOCUMENT of the class CL_SOMU_FORM_SERVICES by passing the adobe form name and LT_KEYS</LI><LI>Pass back the PDF as rawstring from LV_CONTENT to the custom entity.</LI></UL><P>For exposing the custom entity, create a separate Service Definition and Service Binding –</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_19-1715673968549.png" style="width: 497px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109996i3B3FF1BD91DDA568/image-dimensions/497x87?v=v2" width="497" height="87" role="button" title="SachinArtani_19-1715673968549.png" alt="SachinArtani_19-1715673968549.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_20-1715673968552.png" style="width: 739px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109997i0663AC613552E186/image-dimensions/739x225?v=v2" width="739" height="225" role="button" title="SachinArtani_20-1715673968552.png" alt="SachinArtani_20-1715673968552.png" /></span></P><P>The reason why we created a separate Service Definition and Service Binding is because the OData Binding must have a root and only it’s child entities. If we add a custom entity in the binding which is not related to the root entity through association or consumption, we will get the error – “Service ZSAC_UI_PURCHASE_ORDER does not have a unique top node” when we call the adobe form.</P><P>To test the OData service, you can check in T-code: /IWFND/GW_CLIENT.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="SachinArtani_21-1715673968558.png" style="width: 784px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/109998i1C747546000A1CB9/image-dimensions/784x336?v=v2" width="784" height="336" role="button" title="SachinArtani_21-1715673968558.png" alt="SachinArtani_21-1715673968558.png" /></span></P><P><STRONG><U>Consume driver OData service in UI5:</U></STRONG></P><P>For consuming the OData service developed above in a Fiori application, you can write your own logic. To understand how PDF can be previewed, you could refer below blog –</P><P><A href="https://community.sap.com/t5/technology-blogs-by-sap/preview-download-fragmented-forms-from-fiori-list-report-part-2/ba-p/13547933" target="_blank">https://community.sap.com/t5/technology-blogs-by-sap/preview-download-fragmented-forms-from-fiori-list-report-part-2/ba-p/13547933</A></P><P>If you are wondering, how to call adobe form having a master form template assigned? Refer the same blog mentioned above for added syntax.</P><P>As you may know that we cannot create a custom OData service and assign it to any form from the Fiori app ‘Maintain Form Templates’. If we are not using on-premise system, it is not possible to opt for the custom solution we developed above. It’s one of the reason SAP does not recommend creating custom data provider OData service in the first place.</P><P>Thanks for reading it.</P> 2024-05-14T10:53:12.611000+02:00 https://community.sap.com/t5/technology-blogs-by-sap/how-to-enable-automatic-transport-determination-for-the-rap-bo-of-a/ba-p/13650955 How to enable automatic transport determination for the RAP BO of a customizing table 2024-05-18T18:00:00.023000+02:00 patrick_winkler https://community.sap.com/t5/user/viewprofilepage/user-id/729521 <H1 id="toc-hId-861541067">Introduction</H1><P>If you use the ADT wizard to generate a business configuration maintenance object, the automatic transport determination is enabled by default as of release 2405.&nbsp;<SPAN>When the&nbsp;</SPAN>Edit<SPAN>&nbsp;action is performed, a customizing transport request is determined automatically. You can find the determination logic in the ABAP documentation of method get_transport_request of interface if_mbc_cp_rap_tdat_cts.</SPAN></P><P><SPAN>This blog shows you how to migrate an existing maintenance object to enable the automatic transport determination in edit mode.</SPAN></P><P>If you do not want automatic transport determination, you can choose the option "Manual" instead of "Manual with preselection" when generating a new maintenance object in the wizard.</P><P>This blog is relevant for:</P><UL><LI><A class="" href="https://community.sap.com/t5/c-khhcw49343/SAP+BTP%25252C+ABAP+environment/pd-p/73555000100800001164" target="_blank">SAP BTP, ABAP environment</A></LI><LI><A class="" href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA+Public+Cloud/pd-p/08e2a51b-1ce5-4367-8b33-4ae7e8b702e0" target="_blank">SAP S/4HANA Public Cloud</A></LI></UL><P>Further reading:</P><UL><LI><A href="https://community.sap.com/t5/tag/business%20configuration%20maintenance%20object/tg-p/board-id/technology-blog-sap" target="_blank">Related blog posts</A></LI><LI>Learn how you can use<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap?url_id=text-sapcommunity-prdteng-ABAP" target="_blank" rel="noopener noreferrer">ABAP technology</A><SPAN>&nbsp;</SPAN>to develop innovative applications and business solutions across SAP’s portfolio on<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap" target="_blank" rel="noopener noreferrer">SAP Learning Site</A>.</LI></UL><H1 id="toc-hId-665027562">Behavior definition and implementation</H1><P>In the behavior definition, add the annotation "with additional implementation" to the draft action Edit:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>draft action ( features : instance ) Edit with additional implementation;</code></pre><P>&nbsp;</P><P>Save and activate. Place the cursor on Edit and use the quick assist (Ctrl+1) to generate the missing method implementation.<BR />If you use a transport object, implement the method as follows. Adjust the placeholders my_singleton_entity_name and my_singleton_entity_alias accordingly.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code> METHOD EDIT. CHECK lhc_rap_tdat_cts=&gt;get( )-&gt;is_transport_mandatory( ). DATA(transport_request) = lhc_rap_tdat_cts=&gt;get( )-&gt;get_transport_request( ). IF transport_request IS NOT INITIAL. MODIFY ENTITY IN LOCAL MODE my_singleton_entity_name EXECUTE SelectCustomizingTransptReq FROM VALUE #( ( %IS_DRAFT = if_abap_behv=&gt;mk-on SingletonID = 1 %PARAM-transportrequestid = transport_request ) ). reported-my_singleton_entity_alias = VALUE #( ( %IS_DRAFT = if_abap_behv=&gt;mk-on SingletonID = 1 %MSG = mbc_cp_api=&gt;message( )-&gt;get_transport_selected( transport_request ) ) ). ENDIF. ENDMETHOD.</code></pre><P>&nbsp;</P><P><SPAN>If the method IS_TRANSPORT_MANDATORY is not available because you do not use a transport object, use the following source code instead. Prior to release 2402, the ADT wizard did not generate a transport object by default.&nbsp;Adjust the placeholders my_singleton_entity_name, my_basis_table and my_singleton_entity_alias accordingly.</SPAN></P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code> METHOD EDIT. TRY. DATA(task_or_request) = cl_bcfg_cd_reuse_api_factory=&gt;get_transport_service_instance( iv_objectname = my_basis_table iv_objecttype = cl_bcfg_cd_reuse_api_factory=&gt;simple_table )-&gt;get_transport_request( )-&gt;get_task( ). IF task_or_request IS NOT INITIAL. DATA(transport_request) = xco_cp_cts=&gt;transport-&gt;for( task_or_request )-&gt;get_request( )-&gt;value. ENDIF. CATCH cx_bcfg_transport_request cx_xco_runtime_exception. CLEAR transport_request. ENDTRY. IF transport_request IS NOT INITIAL. MODIFY ENTITY IN LOCAL MODE my_singleton_entity_name EXECUTE SelectCustomizingTransptReq FROM VALUE #( ( %IS_DRAFT = if_abap_behv=&gt;mk-on SingletonID = 1 %PARAM-transportrequestid = transport_request ) ). reported-my_singleton_entity_alias = VALUE #( ( %IS_DRAFT = if_abap_behv=&gt;mk-on SingletonID = 1 %MSG = mbc_cp_api=&gt;message( )-&gt;get_transport_selected( transport_request ) ) ). ENDIF. ENDMETHOD.</code></pre><P>&nbsp;</P> 2024-05-18T18:00:00.023000+02:00 https://community.sap.com/t5/technology-blogs-by-sap/sap-fiori-elements-feature-showcase-app-with-rap-for-release-2402-why-is-it/ba-p/13610549 SAP Fiori Elements Feature Showcase App with RAP for Release 2402 - Why is it late? 2024-05-20T09:00:00.028000+02:00 JessieCheah https://community.sap.com/t5/user/viewprofilepage/user-id/46342 <P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="What's New in 2402" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67781iC7D7E867FD4A2CA4/image-size/large?v=v2&amp;px=999" role="button" title="fsa_2402.jpg" alt="What's New in 2402" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">What's New in 2402</span></span></FONT></P><P data-unlink="true"><FONT color="#000000">Wait a minute,&nbsp;SAP BTP, ABAP Environment (aka Steampunk) is already on Release 2405, what do you mean that the&nbsp;<A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase" target="_self" rel="nofollow noopener noreferrer">Feature Showcase App</A>&nbsp;(henceforth shortened to FSA) is only on release 2402?</FONT></P><P><FONT color="#000000">Due to some unforeseen circumstances, we were unable to release FSA earlier according to schedule. So even though Steampunk is on 2405, the new features described here will only include those released with 2402.</FONT><FONT color="#000000"><SPAN>&nbsp;</SPAN></FONT><FONT color="#000000">If you are new to the scene, read <A href="https://blogs.sap.com/2022/12/19/the-sap-fiori-elements-feature-showcase-with-rap-and-abap-cds-annotations/" target="_blank" rel="noopener noreferrer">this blog post</A> to find out what exactly is FSA.</FONT></P><P><FONT color="#000000">At the risk of sounding <SPAN>cliché, better late than never.&nbsp;</SPAN>Let's begin!</FONT></P><H2 id="toc-hId-986925831"><FONT color="#000000">RAP Treeview</FONT></H2><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Ignore the Folder Size in GB, it obviously doesn't tally up..." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67782iC43AB1A22F1AF9E4/image-size/large?v=v2&amp;px=999" role="button" title="treeview.jpg" alt="Ignore the Folder Size in GB, it obviously doesn't tally up..." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Ignore the Folder Size in GB, it obviously doesn't tally up...</span></span></FONT></P><P><FONT color="#000000"><SPAN>In RAP, it is possible to display a CDS Hierarchy as a readonly treeview </SPAN><SPAN>in the object page. All you need is a database table, a base CDS view and a hierarchy. We show you this in the feature showcase app with a directory.</SPAN></FONT></P><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/06_object_page_content.md#rap-treeview-for-hierarchy" target="_blank" rel="noopener nofollow noreferrer">#RAPTreeview</A><BR /><BR /></FONT></P><H2 id="toc-hId-790412326"><FONT color="#000000">Export RAP Treeview</FONT></H2><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="What's your Favourite Colour" style="width: 588px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67787iE802CC9643E3E7EE/image-size/large?v=v2&amp;px=999" role="button" title="export_treeview.jpg" alt="What's your Favourite Colour" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">What's your Favourite Colour</span></span></FONT></P><P><FONT color="#000000">&nbsp;After building your hierarchy, you can now also export it to PDF.</FONT></P><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/01_general_features.md#export-treeview-to-pdf" target="_blank" rel="noopener nofollow noreferrer">#ExportTreeview</A><BR /><BR /></FONT></P><H2 id="toc-hId-593898821"><FONT color="#000000">UI.adaptationHidden</FONT></H2><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Lock Up your UI Components..." style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67783i09372F028B85BDFB/image-size/large?v=v2&amp;px=999" role="button" title="adaptation_hidden.jpg" alt="Lock Up your UI Components..." /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Lock Up your UI Components...</span></span></FONT></P><P><FONT color="#000000"><SPAN>UI adaptation is a feature of SAPUI5 flexibility that allows key users without technical knowledge to easily make UI changes for all users of an app, end users to personalize controls, and developers to extend the UIs of SAPUI5 applications.</SPAN></FONT></P><P><FONT color="#000000"><SPAN>However if you do not want to allow users this flexibility, it is possible to exclude UI components via an annotation.</SPAN></FONT></P><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/01_general_features.md#adaptationhidden" target="_blank" rel="noopener nofollow noreferrer">#UI.adaptationHidden</A><BR /><BR /></FONT></P><H2 id="toc-hId-397385316"><FONT color="#000000">RAP Streams as Read-Only</FONT></H2><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Protect your Upload from Being Edited" style="width: 530px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67785i972E7A0CC2D47E6D/image-size/large?v=v2&amp;px=999" role="button" title="lr_stream_static_fc.jpg" alt="Protect your Upload from Being Edited" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Protect your Upload from Being Edited</span></span></FONT></P><P><FONT color="#000000">You can enable static or dynamic feature control for your stream properties at the list report or in the object page.</FONT></P><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/03_list_report_content.md#static-field-control-for-large-objectstream-property" target="_blank" rel="noopener nofollow noreferrer">#StreamStaticFeatureCtrl-LR</A><BR /><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/03_list_report_content.md#dynamic-field-control-for-large-objectstream-property" target="_self" rel="nofollow noopener noreferrer">#StreamDynamicFeatureCtrl-LR</A><BR /><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/06_object_page_content.md#static-field-control-for-large-objectstream-property" target="_self" rel="nofollow noopener noreferrer">#StreamStaticFeatureCtrl-OP</A><BR /><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/06_object_page_content.md#dynamic-field-control-for-large-objectstream-property" target="_self" rel="nofollow noopener noreferrer">#StreamDynamicFeatureCtrl-OP</A>&nbsp;<BR /><BR /></FONT></P><H2 id="toc-hId-200871811"><FONT color="#000000">RAP Default Value Functions</FONT></H2><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Now Default Values can be Filled In Programatically" style="width: 448px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67789i1B115321A6966A0B/image-size/large?v=v2&amp;px=999" role="button" title="default_function_create.jpg" alt="Now Default Values can be Filled In Programatically" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Now Default Values can be Filled In Programatically</span></span></FONT></P><P><FONT color="#000000">With the annotation UI.defaultValue we were able to set a default value for our action parameters, but it was only either a constant or using an existing value from a reference value. N<SPAN>ow with the new default functions, you can define in the behaviour implementation&nbsp;<EM>what</EM> the default values should be, calculated or otherwise. And this works for create, create-by-association and also actions.</SPAN></FONT></P><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/01_general_features.md#default-values-function" target="_blank" rel="noopener nofollow noreferrer">#DefaultFunction-ForCreate, -ForAction, -ForCBA</A><BR /><BR /></FONT></P><H2 id="toc-hId-4358306"><FONT color="#000000">Hiding Datapoints</FONT></H2><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Playing Hide and Seek" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67792iF36593753948B3C9/image-size/large?v=v2&amp;px=999" role="button" title="datapoint_hidden.jpg" alt="Playing Hide and Seek" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Playing Hide and Seek</span></span></FONT></P><P><FONT color="#000000"><SPAN>If you are showing data points in the sections of your object page, it is possible to hide them using annotation UI.dataPoint.hidden. Beware of the&nbsp;<A href="https://github.tools.sap/ABAP-for-HANA-Programming-Model/fiori-feature-showcase-app/wiki/Identified-Constraints" target="_self" rel="nofollow noopener noreferrer">constraints</A>&nbsp;though, as UI does not support this for Header facet yet.</SPAN></FONT></P><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/04_object_page_general.md#hiding-data-points" target="_blank" rel="noopener nofollow noreferrer">#DatapointHidden</A><BR /><BR /></FONT></P><H2 id="toc-hId--192155199"><FONT color="#000000">RAP Simple Types</FONT></H2><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Simple to Use" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67793iC4B00B3EEC258AA0/image-size/large?v=v2&amp;px=999" role="button" title="simple_type_label.jpg" alt="Simple to Use" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Simple to Use</span></span></FONT></P><P><FONT color="#000000">You<SPAN>&nbsp;can create user-defined data types in ABAP CDS, which work similarly to Data Elements in DDIC, with the added advantage of CDS annotations.</SPAN></FONT></P><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/01_general_features.md#cds-simple-type" target="_blank" rel="noopener nofollow noreferrer">#SimpleType</A><BR /><BR /></FONT></P><H2 id="toc-hId--388668704"><FONT color="#000000">Additional Side Effects</FONT></H2><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="se-self-a-entity.gif" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67808i39CE86EA6AA51DF9/image-size/large?v=v2&amp;px=999" role="button" title="se-self-a-entity.gif" alt="se-self-a-entity.gif" /></span></FONT></P><P><FONT color="#000000">2 additional side effects possibility are showcased in one new example:</FONT></P><UL><LI><FONT color="#000000">$self as Source</FONT></LI><LI><FONT color="#000000">permission of association as Target</FONT></LI></UL><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/04_object_page_general.md#self-affects-permission-of-association" target="_blank" rel="noopener nofollow noreferrer">#SelfAffectsPermissionAssociation</A></FONT></P><H3 id="toc-hId--456099490"><FONT color="#000000">Bonus - Entity affects Field</FONT></H3><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Worka, workaround" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67846iC1FA0955E1B80F32/image-size/large?v=v2&amp;px=999" role="button" title="se-entity-a-field-workaround.gif" alt="Worka, workaround" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">Worka, workaround</span></span></FONT></P><P><FONT color="#000000">One use case that we encountered was the possibility to change a field via side effect, after an instance of an associated entity was deleted. While there is currently no syntax to cater for this, a workaround was found.</FONT></P><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/04_object_page_general.md#entity-affects-field-workaround" target="_blank" rel="noopener nofollow noreferrer">#SideEffectWorkaround</A><BR /><BR /></FONT></P><H2 id="toc-hId--781695714"><FONT color="#000000">What else is new?</FONT></H2><P><FONT color="#000000"><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="More Structure means More to Click Through" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/67822iF42AD5FEAC381172/image-size/large?v=v2&amp;px=999" role="button" title="guide.jpg" alt="More Structure means More to Click Through" /><span class="lia-inline-image-caption" onclick="event.preventDefault();">More Structure means More to Click Through</span></span></FONT></P><P><FONT color="#000000">The <A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/wiki/Feature-Showcase-App-Guide" target="_self" rel="nofollow noopener noreferrer">Guide</A> that you are used to has now been updated into a new format, which 6 sections describing the different features, links available from README. If you are however used to the old format and search function, the Wiki is still updated with new contents.</FONT></P><P><FONT color="#000000"><span class="lia-unicode-emoji" title=":backhand_index_pointing_up:">👆</span><A href="https://github.com/SAP-samples/abap-platform-fiori-feature-showcase/blob/main/README.md" target="_blank" rel="noopener nofollow noreferrer">#README</A><BR /><BR /></FONT></P><H3 id="toc-hId--924357869"><FONT color="#000000">A gentle reminder</FONT></H3><P><FONT color="#000000">If you already have the previous version of the app in your system, run the data generator after you have done the pull and activated the objects.&nbsp;</FONT><FONT color="#000000">Feedback and comments are as always very much welcomed. See you next release!</FONT></P> 2024-05-20T09:00:00.028000+02:00 https://community.sap.com/t5/technology-blogs-by-members/our-way-into-the-clean-core/ba-p/13704482 Our Way into the Clean Core 2024-05-21T09:42:10.210000+02:00 felix_grab https://community.sap.com/t5/user/viewprofilepage/user-id/444338 <DIV><DIV><DIV><H2 id="toc-hId-1014749781">Where are we coming from</H2><P>We are a long-standing software partner of SAP, specializing in supply chain AddOns for nearly 20 years. We have different solutions inside of the portfolio and the older ones are more procedural coding, while the newer ones have more object oriented, more unit testing, and so on. But the big majority of all user interfaces are ALV Grid, ALV Tree and graphics (based on cl_gui_chart_engine). By the time we developed our own framework for these tools, so that it is easy to create several new containers with ALVs, letting the user decide, if he wants to use docking containers or dialogbox containers, support multi screens and so on.</P><P>In essence, we had a user interface that our end users greatly appreciated due to its ease of use. However, despite creating additional UI5 applications in recent years, most of our current users continue to work primarily within the SAP GUI. As a result, our initial plans were to gradually transition to more UI5-based applications. But the pressure to do so wasn’t significant, at least not from our customer base.</P><P>The majority of our customers continue to operate on onPremise systems. However, we also serve a significant number of private cloud customers. Interestingly, we have only one customer currently using a public cloud system. As for the software versions, most customers are still on ECC 6.0, although many are currently undergoing S/4 conversion projects. Despite this, we haven’t observed a substantial shift toward public cloud scenarios. But some customers are transitioning from their on-premise systems to private cloud environments.</P><H2 id="toc-hId-818236276"><SPAN>Steampunk and embedded steampunk</SPAN></H2><P><SPAN>Upon learning about steampunk in the context of public cloud scenarios, we decided to explore its potential. However, our assessment revealed more disadvantages than benefits for our specific situation.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Whitelisted APIs posed challenges, being slow and not always designed to meet our specific needs. Additionally, data availability was an issue—critical data often remained inaccessible via the whitelisted APIs.</SPAN><SPAN>&nbsp;</SPAN></P><P><SPAN>Our unique challenge lies in providing highly integrative solutions that primarily operate on SAP documents, MRP results, capacities, production orders, and more. Unlike scenarios where data is merely replicated from the ERP&nbsp;system, our solution often involves real-time interactions with orders, order conversions, and other dynamic processes. Unfortunately, this doesn’t align well with the steampunk approach.</SPAN><SPAN>&nbsp;</SPAN></P><P>In 2021, we encountered the first information about embedded steampunk. A <A href="https://community.sap.com/t5/technology-blogs-by-sap/steampunk-is-going-all-in/ba-p/13519603" target="_blank">blog post</A> featured the following picture, which caught our attention:</P><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="felix_grab_0-1715932877762.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/111890iABB5C0A9B79A6CF5/image-size/large?v=v2&amp;px=999" role="button" title="felix_grab_0-1715932877762.png" alt="felix_grab_0-1715932877762.png" /></span><P>Direct access to (released) CDS-Views would give us the opportunity to read data in a fast way and without the need to replicate it onto other systems sounded good. Also BAdI usage was announced, RAP as the programming model, and maybe one codeline for onPrem, private and public cloud was also interesting.</P><H2 id="toc-hId-621722771">Clean core project with SAP</H2><P>And then in late 2022 we heard that SAP is looking for customers and partners, which want to participate in a pilot project for clean core development on private cloud systems. This would give us the opportunity to get more experience with clean core development, would get a glimpse at the new tools like the landscape portal, learn more about the changing software development process, etc. That sounded great, so we picked an smaller part of our application, where details of purchasing requirements are shown, with additional alerts, additional text functions, functions for releasing, source determination and conversion into purchase orders. That looked good as a first example, with which we would get our first experiences.</P><P>So we began to discuss the approach with the SAP project team members, started development and the SAP colleagues always helped us, when we were stuck, didn't know how special parts had to be implemented to fit the clean core requirements, etc.</P><H2 id="toc-hId-425209266">Namespace and package structure</H2><P>Our initial step involved creating a new namespace and package structure to house all our new code. As the project progressed and information about Tier 2 became more detailed, we also established an additional namespace and package structure specifically for Tier 2 development. In summary:</P><UL><LI><SPAN>Tier 1: We maintain a separate namespace and package structure for Tier 1 components. All packages within the Tier 1 software component use the </SPAN><SPAN>“</SPAN><SPAN>ABAP for Cloud Development</SPAN><SPAN>”</SPAN><SPAN> language version.</SPAN></LI><LI><SPAN>Tier 2: A distinct namespace and package structure is dedicated to Tier 2 development. The language version for Tier 2 packages remains as </SPAN><SPAN>“</SPAN><SPAN>Standard ABAP.</SPAN><SPAN>”</SPAN></LI><LI><SPAN>Our old legacy AddOn continues to exist as Tier 3.</SPAN></LI></UL><P><SPAN>To change the ABAP language version to “ABAP for Cloud,” execute the</SPAN><SPAN> report "RSMAINTAIN_SWCOMPONENTS" (see screenshot).<BR /></SPAN>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="felix_grab_1-1715932877768.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/111888iA885938841BC525D/image-size/large?v=v2&amp;px=999" role="button" title="felix_grab_1-1715932877768.png" alt="felix_grab_1-1715932877768.png" /></span><H2 id="toc-hId-228695761">Looking for the released APIs</H2><P>The next step was, that we had to look for released APIs, to get our data read. For this we normally start on <A href="https://api.sap.com/" target="_blank" rel="noopener noreferrer">api.sap.com</A> and then looking into the Product "SAP S/4HANA Cloud Private Edition" and checking the CDS View tab. This often helps, but sometimes it can get complicated to find the correct object. For example we were looking for the missing part indicator in the reservations. We quickly found reservation data, but this didn't offer the missing part indicator.</P><P>So we created an Customer-Influence Request (CI-Request), to get this field added to the existing CDS view. But the reply was, that we should use the CDS views of the planned order and production order, because the information is available there. Additionally we created a little helper report, where we could enter the old table and field name, which then checks for released or not released CDS views, which container this field.</P><P>When it got more clear, how the process is working with Tier 2, additionally the <A href="https://sap.github.io/abap-atc-cr-cv-s4hc/?version=objectReleaseInfo_PCE2023_1.json" target="_blank" rel="noopener nofollow noreferrer">Cloudification Repository Viewer</A> can be helpful, because you could easily look up, if an object is already released for Tier 1 or Tier 2.</P><P>We additionally decided to create an internal list to keep track of all our topics, the successor objects, CI-Request numbers for public and private cloud, etc.</P><P>But in total we will have to create most likely a few hundred CI-Request until we get most of our application transferred to the clean core and we will have to see, if some of our application won't be able to work in a clean core environment.</P><H2 id="toc-hId-32182256">Time delays and non-strategic solutions</H2><P>CI-Request take time… When we created CI-Request the response time was quite different. Some got early feedback, some took several weeks. And the majority of them, didn't get positive results immediately. Many needed additional information, why the information is important for us, and some were declined, because the information was belonging to solutions, which are not strategic for SAP, but still usable by our customers. This is of course problematic for us, because we cannot offer our customers a solution, if we don't get the data, even if the SAP solution is not officially obsolete.</P><P>Additionally it could take quite a long time, even if SAP is willing to modify or create an API for us. Let's sketch an example:</P><UL><LI><SPAN>We want to offer a solution to private cloud and onPrem customers and an API is missing</SPAN></LI><LI><SPAN>We request this API on may 1st, 2024</SPAN></LI><LI><SPAN>SAP has some questions, but let's say give us a positive feedback two month later. After a positive feedback, it could of course take time to develop this API and this could of course interfere with the deadlines for specific SAP release cycles. But let's assume it will be directly available in the next private cloud and the next onPrem Release</SPAN></LI><LI><SPAN>With the current 2 year release cycle onPrem, this would be fall 2025</SPAN></LI><LI><SPAN>To use it for our own developments we would have to upgrade our own development system onto this level and would ideally ship the first product which is using this in 2026</SPAN></LI><LI><SPAN>This scenario is quite positive, depending on release cycles of SAP or ourselves it could be easily later…</SPAN></LI></UL><P>And last but not least, the customer also have to be on SAP release 2025 and have our latest release installed. So from a todays point of view, it takes additional years until maybe half of our customers can use this function…</P><H2 id="toc-hId--164331249">Tier 2</H2><P>At least for some of these problems Tier 2 can be a solution. Tier 2 basically says, that SAP allows us the use of functionality, which has not been designed for clean core, but can still be used, until SAP has a new API available. So old function modules, database tables, and so on could get a tier 2 release, so that we might not have the above stated problem, but directly can use Tier 2, but also preparing our Tier 2 coding, so that it could later be easily replaced with the new API.</P><P>This tier 2 will be an own software component, where only the neccessary Tier 2 parts are available, and which releases it's data for the Tier 1 and Tier 3 software component, which basically results in the following installation sequence for customers:</P><UL><LI>Tier 2</LI><LI>Tier 1</LI><LI>Tier 3 (old legacy AddOn, until we have transferred all of it's functions and applications into Tier 1 and 2)</LI></UL><H2 id="toc-hId--360844754">Success is a process</H2><P>Since our AddOn is quite big and in many parts very integrated into the SAP system, this is not a fast and easy conversion, but it will take years. So our current plan is to transfer functionality year by year into the new solution, certify the new solution and in the first years our customers will normally be installing both solutions, the old legacy addon and the clean core solution, but in some years, maybe the old legacy addon will no longer be neccessary. It's currently not completely clear for us, how much data we will provide in the BTP layer, but there might also be some additional dashboards, etc. on this level. But since our applications are highly integrative the data part will be on-stack almost all of the time.</P><P>In the following graphic you can see he evolution of the feature scope which we are offering, and where the percentage of functions which shall be offered with a clean core certification will increase over the years.</P><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="felix_grab_2-1715932877769.png" style="width: 999px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/111889iF023B54DD7BADDB5/image-size/large?v=v2&amp;px=999" role="button" title="felix_grab_2-1715932877769.png" alt="felix_grab_2-1715932877769.png" /></span><H2 id="toc-hId--557358259">Migrating applications and migrating code</H2><P>When we want to migrate our solutions into the cloud, then it's a challenge on multiple levels. For new applications it might be a bit easier, because you can directly create the concept with regards to the available data and the UI5 possibilities.</P><P>It get's more complicated, when we are looking at old applications, which are still running with ALVs. On the one hand it's not uncommon, that not all of the data is available through released APIs, on the other hand even if the data is available, automatic checks of the current code are often difficult.</P><P>When I analyze the source code of an old application, all the data elements, structures, etc. which are connected to the old framework (for example ALVs) is not really relevant for me, because if I create it new there will be no ALV for the output. So I am mainly interrested in the selection and the processing of the data. If I am lucky, then data and framework are separated, but at least in our case it is not separated by packages, or something like this, so that I cannot build my object list for ATC checks only on the data level.</P><P>In our case it is normally separated through methods and form routines, so we did manual checks in these objects, which data we are reading, and checking the APIs afterwards. Since our solutions contains ~5.8 million lines of source code, we don't do this directly at once. Each of our different development teams picks an pilot application out of their product scope, which will be transformed first. Since the first transformations will be slower, due to the learning curve, this pilot application shouldn't be too large.</P><P>Since it is problematic, when we i.e. need 20 not released objects for one of the pilot applications, and we don't get alle important parts released (or released on a different times), we created CI requests with multiple objects as well. Of course this makes only sense, if alle the different objects are from the same SAP module, because otherwise the handling on SAP side would be difficult. But when they are in the same SAP module, this might make sense, so that we might get the objects released in the same time and it is clear, that we have problems if only some are successful.</P><H2 id="toc-hId--753871764">Outlook</H2><P>When we are looking at the clean core project until now, then we still think, that this is the way to go, at least for solutions, which are tightly coupled with ERP processes and data. Otherwise it might make sense to check, if the solution should be created on the BTP.</P><P>But the clean core on-stack development looks good as a replacement for the classical on-stack development. Of course, this topic is still quite new and therefore there are still some problems, but we are seeing that SAP is trying to build up a good development and delivery process for clean core.</P></DIV></DIV></DIV> 2024-05-21T09:42:10.210000+02:00 https://community.sap.com/t5/technology-blogs-by-sap/how-to-implement-additional-authorization-objects-for-the-rap-bo-of-a/ba-p/13710852 How to implement additional authorization objects for the RAP BO of a customizing table 2024-05-24T15:38:39.591000+02:00 patrick_winkler https://community.sap.com/t5/user/viewprofilepage/user-id/729521 <H1 id="toc-hId-886475170">Introduction</H1><P>If you use the <A href="https://help.sap.com/docs/btp/sap-business-technology-platform/generating-business-configuration-maintenance-object-with-generate-abap-repository-objects-wizard?version=Cloud" target="_self" rel="noopener noreferrer">ADT wizard</A> to generate a business configuration maintenance object, the&nbsp;<SPAN>authorization control is based on authorization object</SPAN><SPAN>&nbsp;</SPAN>S_TABU_NAM<SPAN>&nbsp;</SPAN><SPAN>and the CDS view entity name of the base table.</SPAN></P><P><SPAN>This blog shows you how to implement additional authorization objects.</SPAN></P><P>This blog is relevant for:</P><UL><LI><a href="https://community.sap.com/t5/c-khhcw49343/SAP+BTP%25252C+ABAP+environment/pd-p/73555000100800001164" class="lia-product-mention" data-product="11-1">SAP BTP, ABAP environment</a>&nbsp;</LI><LI><a href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA+Cloud+Public+Edition/pd-p/08e2a51b-1ce5-4367-8b33-4ae7e8b702e0" class="lia-product-mention" data-product="1199-1">SAP S/4HANA Cloud Public Edition</a>&nbsp;</LI><LI><a href="https://community.sap.com/t5/c-khhcw49343/SAP+S%25252F4HANA+Cloud+Private+Edition/pd-p/5c26062a-9855-4f39-8205-272938b6882f" class="lia-product-mention" data-product="1198-1">SAP S/4HANA Cloud Private Edition</a></LI></UL><P>Further reading:</P><UL><LI><A href="https://community.sap.com/t5/tag/business%20configuration%20maintenance%20object/tg-p/board-id/technology-blog-sap" target="_blank">Related blog posts</A></LI><LI>Learn how you can use<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap?url_id=text-sapcommunity-prdteng-ABAP" target="_blank" rel="noopener noreferrer">ABAP technology</A><SPAN>&nbsp;</SPAN>to develop innovative applications and business solutions across SAP’s portfolio on<SPAN>&nbsp;</SPAN><A href="https://learning.sap.com/products/business-technology-platform/development/abap" target="_blank" rel="noopener noreferrer">SAP Learning Site</A>.</LI></UL><H1 id="toc-hId-689961665">Use case 1: no CDS elements of the CDS entity are relevant for the access condition</H1><P>You want to use additional authorization objects that are independent of the CDS elements of the CDS entity. This means that the configuration user can either read/edit all entities or none. This setup is recommended as the configuration user should not only work with a subset of configuration entries.</P><P>Proceed as follows:</P><P>1. Enhance the generated access control for the table entity. In this example, the authorization object ZAO_PROFILE is added.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@MappingRole: true define role ZI_Example { grant select on ZI_Example where ( ) = ASPECT pfcg_auth ( 'S_TABU_NAM', ACTVT = '03', TABLE = 'ZI_EXAMPLE' ) AND ( ) = ASPECT pfcg_auth ( 'ZAO_PROFILE', ACTVT = '03', PROFILE = 'CONFIGURATION' ) ; }</code></pre><P>&nbsp;</P><P>2. In the generated behavior implementation class, enhance all authorization checks:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code> METHOD get_global_authorizations. DATA(is_authorized) = if_abap_behv=&gt;auth-unauthorized. AUTHORITY-CHECK OBJECT 'S_TABU_NAM' ID 'TABLE' FIELD 'ZI_EXAMPLE' ID 'ACTVT' FIELD '02'. IF sy-subrc = 0. AUTHORITY-CHECK OBJECT 'ZAO_PROFILE' ID 'PROFILE' FIELD 'CONFIGURATION' ID 'ACTVT' FIELD '02'. IF sy-subrc = 0. is_authorized = if_abap_behv=&gt;auth-allowed. ENDIF. ENDIF. result-%update = is_authorized. result-%action-Edit = is_authorized. result-%action-SelectCustomizingTransptReq = is_authorized. ENDMETHOD.</code></pre><P>&nbsp;</P><H1 id="toc-hId-493448160">Use case 2: CDS elements of the CDS entity are relevant for the access condition</H1><P>You want to use additional authorization objects that refer to the CDS elements of the CDS entity. This means that the configuration user may only be able to work with a subset of the configuration entries.</P><H2 id="toc-hId-426017374">Example objects</H2><P>The example table for plants contains the field REGION (EMEA, NA, ...) to be used for authorization control.<BR />The configuration expert shall only work with plants from a specific region.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label : 'Plant' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #C @AbapCatalog.dataMaintenance : #ALLOWED define table zplant { key client : abap.clnt not null; key plant_id : abap.numc(3) not null; region : zd_region; last_changed_at : abp_lastchange_tstmpl; local_last_changed_at : abp_locinst_lastchange_tstmpl; }</code></pre><P>&nbsp;</P><P>For this purpose, an authorization object ZAO_REGION with the authorization field ZAF_REGION is created based on the data element ZD_REGION.</P><H2 id="toc-hId-229503869">Adjust Authorization Control Object</H2><P>Add the authorization object ZAO_REGION to the generated access control object of the table entity. This access control restricts read access to the data returned by the CDS view. For example, what you can see when you open the Custom Business Configurations app in read-only mode.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@MappingRole: true define role ZI_Plant { grant select on ZI_Plant where ( ) = ASPECT pfcg_auth ( 'S_TABU_NAM', ACTVT = '03', TABLE = 'ZI_PLANT' ) and ( Region ) = ASPECT pfcg_auth ( 'ZAO_REGION', ACTVT = '03', ZAF_REGION ) ; }</code></pre><P>&nbsp;</P><H2 id="toc-hId-32990364">Create Draft Query view</H2><P>When you switch to edit mode, a draft version is created for all entities. Since the access control for the CDS view is not applied to the draft table, the user can see the draft version for entities that they would not see in display mode. To restrict read access for the draft entity, a <A href="https://help.sap.com/docs/abap-cloud/abap-rap/draft-query-views" target="_self" rel="noopener noreferrer">draft query view</A> is required.</P><P>1. Right-click the draft entity table and choose New Data Definition.<BR />2. Enter ZR_PLANT as Name and Draft Query View: Plant as Description.<BR />3. Select defineViewEntity as a template.<BR />4. Set authorizationCheck to CHECK.</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@AccessControl.authorizationCheck: #CHECK @EndUserText.label: 'Draft Query View: Plant' define view entity ZR_PLANT as select from zplant_d { key plantid as Plantid, region as Region, lastchangedat as Lastchangedat, locallastchangedat as Locallastchangedat, singletonid as Singletonid, draftentitycreationdatetime as Draftentitycreationdatetime, draftentitylastchangedatetime as Draftentitylastchangedatetime, draftadministrativedatauuid as Draftadministrativedatauuid, draftentityoperationcode as Draftentityoperationcode, hasactiveentity as Hasactiveentity, draftfieldchanges as Draftfieldchanges }</code></pre><P>&nbsp;</P><P>Create an Authorization Control Object for the Draft Query View:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Mapping Role for ZR_Plant' @MappingRole: true define role ZR_PLANT { grant select on ZR_PLANT where inheriting conditions from entity ZI_Plant; }</code></pre><P>&nbsp;</P><P>In the behavior definition add the query annotation to the draft table:</P><pre class="lia-code-sample language-abap"><code>draft table zplant_d query ZR_PLANT</code></pre><H2 id="toc-hId--163523141">Implement Prechecks for Operations</H2><P>It must be prevented that the configuration user can create/update/delete plants for regions for which they are not authorized. This can be achieved with a <A href="https://help.sap.com/docs/abap-cloud/abap-rap/operation-precheck?q=precheck" target="_self" rel="noopener noreferrer">pre-check of the operation</A>.</P><P>In the behavior definition, add the annotation precheck to the action create in the singleton entity and the action update, delete in the table entity:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>association _Plant { create ( features : instance, precheck ); with draft; } update( features : global, precheck ); delete( features : global, precheck );</code></pre><P>&nbsp;</P><P>Place the cursor on the actions and use the quick assist (CTRL+1) to create the missing method implementations:</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code> METHOD precheck_cba_Plant. LOOP AT entities ASSIGNING FIELD-SYMBOL(&lt;entity&gt;). LOOP AT &lt;entity&gt;-%target ASSIGNING FIELD-SYMBOL(&lt;plant&gt;). AUTHORITY-CHECK OBJECT 'ZAO_REGION' ID 'ZAF_REGION' FIELD &lt;plant&gt;-Region ID 'ACTVT' FIELD '01'. CHECK sy-subrc &lt;&gt; 0. APPEND VALUE #( %cid = &lt;plant&gt;-%cid ) TO failed-plant. APPEND VALUE #( %cid = &lt;plant&gt;-%cid %element-Region = if_abap_behv=&gt;mk-on %msg = new_message_with_text( text = |Not authorized to create plants in region { &lt;plant&gt;-region }| ) ) TO reported-plant. ENDLOOP. ENDLOOP. ENDMETHOD.</code></pre><pre class="lia-code-sample language-abap"><code> METHOD precheck_update. LOOP AT entities ASSIGNING FIELD-SYMBOL(&lt;plant&gt;). AUTHORITY-CHECK OBJECT 'ZAO_REGION' ID 'ZAF_REGION' FIELD &lt;plant&gt;-Region ID 'ACTVT' FIELD '02'. CHECK sy-subrc &lt;&gt; 0. APPEND VALUE #( %cid = &lt;plant&gt;-%cid_ref ) TO failed-plant. APPEND VALUE #( %cid = &lt;plant&gt;-%cid_ref %element-Region = if_abap_behv=&gt;mk-on %msg = new_message_with_text( text = |Not authorized to change plants in region { &lt;plant&gt;-region }| ) ) TO reported-plant. ENDLOOP. ENDMETHOD. METHOD precheck_delete. READ ENTITY IN LOCAL MODE zi_plant FIELDS ( region ) WITH CORRESPONDING #( keys ) RESULT DATA(plants). LOOP AT plants ASSIGNING FIELD-SYMBOL(&lt;plant&gt;). AUTHORITY-CHECK OBJECT 'ZAO_REGION' ID 'ZAF_REGION' FIELD &lt;plant&gt;-Region ID 'ACTVT' FIELD '06'. CHECK sy-subrc &lt;&gt; 0. READ TABLE keys WITH KEY draft COMPONENTS %tky = &lt;plant&gt;-%tky ASSIGNING FIELD-SYMBOL(&lt;key&gt;). APPEND VALUE #( %cid = &lt;key&gt;-%cid_ref ) TO failed-plant. APPEND VALUE #( %cid = &lt;key&gt;-%cid_ref %element-Region = if_abap_behv=&gt;mk-on %msg = new_message_with_text( text = |Not authorized to delete plants in region { &lt;plant&gt;-region }| ) ) TO reported-plant. ENDLOOP. ENDMETHOD.</code></pre><P>&nbsp;</P><P>Consider using the authorization control also for value helps, for example, the user can only select the region for which he or she is authorized. Consider adapting or using <A href="https://help.sap.com/docs/abap-cloud/abap-rap/operation-defaulting" target="_self" rel="noopener noreferrer">operation defaulting</A> so that, for example, the region field is set to the region for which the user is authorized when creating a new plant.</P> 2024-05-24T15:38:39.591000+02:00 https://community.sap.com/t5/technology-blogs-by-members/develop-an-application-utilizing-the-rap-web-api-with-sap-build-apps/ba-p/13732316 Develop an application utilizing the RAP Web API with SAP Build Apps framework in S4Hana OnPremise 2024-06-16T13:17:48.896000+02:00 anjan_paul https://community.sap.com/t5/user/viewprofilepage/user-id/183963 <P>In this blog it will be shown how SAP Restful Application Programming Model can be used and consumed in SAP Build App to create an APP.</P><P><STRONG>Technology used:</STRONG></P><OL><LI>SAP RAP</LI><LI>SAP Build Apps</LI><LI>SAP BTP</LI></OL><P>&nbsp;</P><P><STRONG>Development Steps:</STRONG></P><P>&nbsp; &nbsp; 1.&nbsp; Create RAP Model objects in Managed scenario.</P><UL><LI>&nbsp;Database Table</LI><LI>Interface View</LI><LI>Transactional View</LI><LI>Consumption View</LI><LI>Transactional Behavior Definition</LI><LI>Consumption Behavior Definition</LI><LI>Service Definition</LI><LI>Service Binding</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_0-1718535102595.png" style="width: 645px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124174i18EFE0D063FC9CF3/image-dimensions/645x246?v=v2" width="645" height="246" role="button" title="anjan_paul_0-1718535102595.png" alt="anjan_paul_0-1718535102595.png" /></span></P><P>&nbsp;</P><P>&nbsp; &nbsp; &nbsp;2. The Service Binding should be published as Web API.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_1-1718535102603.png" style="width: 759px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124175i05B4C51E378D78E1/image-dimensions/759x243?v=v2" width="759" height="243" role="button" title="anjan_paul_1-1718535102603.png" alt="anjan_paul_1-1718535102603.png" /></span></P><P>&nbsp; &nbsp;3. Create a Destination .</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_10-1718535221718.png" style="width: 677px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124184i81B12D3FA32B62BA/image-dimensions/677x308?v=v2" width="677" height="308" role="button" title="anjan_paul_10-1718535221718.png" alt="anjan_paul_10-1718535221718.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp; &nbsp; 4. Create A SAP App in SAP Build Apps using the RAP service.</P><P>&nbsp; &nbsp; &nbsp; &nbsp;<span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_5-1718535102652.png" style="width: 683px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124177iB830B902DE9BCF32/image-dimensions/683x222?v=v2" width="683" height="222" role="button" title="anjan_paul_5-1718535102652.png" alt="anjan_paul_5-1718535102652.png" /></span></P><P>&nbsp; &nbsp; &nbsp; &nbsp; 5. Home Page to Display list of records.</P><P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="anjan_paul_3-1718535102637.png" style="width: 648px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124179iC9D14A45F36DD103/image-dimensions/648x314?v=v2" width="648" height="314" role="button" title="anjan_paul_3-1718535102637.png" alt="anjan_paul_3-1718535102637.png" /></span></P><P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;6. Update Page to Create New Records.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="anjan_paul_4-1718535102647.png" style="width: 696px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124178iA2A3C85D45EF5823/image-dimensions/696x346?v=v2" width="696" height="346" role="button" title="anjan_paul_4-1718535102647.png" alt="anjan_paul_4-1718535102647.png" /></span></P><P>&nbsp;</P><P><STRONG>Test :</STRONG></P><OL><LI>Open the APP in Preview Mode. It will display all the existing records in the Table using RAP service.</LI></OL><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_6-1718535102658.png" style="width: 739px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124182i1BEDE55F4152CF97/image-dimensions/739x255?v=v2" width="739" height="255" role="button" title="anjan_paul_6-1718535102658.png" alt="anjan_paul_6-1718535102658.png" /></span></P><P>&nbsp;</P><P>&nbsp; &nbsp; &nbsp; 2. After click ‘Create New Student’ button Update page will open. Create a New Record.</P><P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_7-1718535102664.png" style="width: 747px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124180i579CB254F4B07989/image-dimensions/747x280?v=v2" width="747" height="280" role="button" title="anjan_paul_7-1718535102664.png" alt="anjan_paul_7-1718535102664.png" /></span></P><P>&nbsp; &nbsp; &nbsp; &nbsp;3. After Create button clicked. New record will be created using RAP Post service.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_8-1718535102671.png" style="width: 747px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124181i36615054845CDE23/image-dimensions/747x295?v=v2" width="747" height="295" role="button" title="anjan_paul_8-1718535102671.png" alt="anjan_paul_8-1718535102671.png" /></span></P><P>&nbsp;</P><P>&nbsp; &nbsp; &nbsp;4. You can also see the data in ADT Data Preview .</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_9-1718535102683.png" style="width: 747px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/124183iD2D0EED2DB8D9866/image-dimensions/747x355?v=v2" width="747" height="355" role="button" title="anjan_paul_9-1718535102683.png" alt="anjan_paul_9-1718535102683.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>This blog summarizes how to utilize RAP services within SAP Build Apps, detailing necessary configurations in SAP BTP.</P><P>&nbsp;</P> 2024-06-16T13:17:48.896000+02:00 https://community.sap.com/t5/technology-blogs-by-members/extending-a-standard-api-a-guide-to-enhancing-api-qualitynotification/ba-p/13742428 Extending a Standard API - A Guide to Enhancing API_QUALITYNOTIFICATION 2024-06-26T18:13:09.567000+02:00 DeepakMalik https://community.sap.com/t5/user/viewprofilepage/user-id/1429003 <P>Hello SAP Community Members,</P><P>We hope this post finds you well.</P><P>While there are few others which generalizes on extending standard APIs, we would like to call out the steps required to extend the <FONT face="courier new,courier"><STRONG>API_QUALITYNOTIFICATION</STRONG></FONT>, should you have the same requirement(s).</P><P>Our goal is to add a field to the standard API response. This process can be adapted to add or extend multiple fields as needed.</P><P><STRONG>Finding the Service Definition for the Standard API</STRONG></P><P>The first step is to locate the Service Definition. For our example, the Service Definition is <FONT face="courier new,courier"><STRONG>API_QUALITYNOTIFICATION</STRONG></FONT>. You can find the Service Definition using the API URL or alternatively, you can use Eclipse to perform a pattern search with the API name.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pasted image 20240620115310.png" style="width: 635px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128591i6151A4D3F4B9A006/image-dimensions/635x197?v=v2" width="635" height="197" role="button" title="Pasted image 20240620115310.png" alt="Pasted image 20240620115310.png" /></span></P><P>Once you have the Service Definition, identify the fields within the entities present in the service definition. For our requirement, we needed to add the <FONT face="courier new,courier"><STRONG>NotificationPriorityText</STRONG></FONT>&nbsp;field. The path for this field is as follows:</P><P><FONT face="courier new,courier">&gt; A_QltyNotification (viewEntity)</FONT><BR /><FONT face="courier new,courier">&nbsp; &gt; R_QltyNotificationTP (viewEntity)</FONT><BR /><FONT face="courier new,courier">&nbsp; &nbsp; &gt; I_QltyNotification (View)</FONT><BR /><FONT face="courier new,courier">&nbsp; &nbsp; &nbsp; &gt; I_Notification (View)</FONT><BR /><FONT face="courier new,courier">&nbsp; &nbsp; &nbsp; &nbsp; &gt; I_NotificationPriority (View)</FONT><BR /><FONT face="courier new,courier">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &gt; I_NotificationPriorityText (View)</FONT><BR /><FONT face="courier new,courier">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &gt; NotificationPriorityText (Field)</FONT><BR /><BR /><STRONG>Creating extendView / extendViewEntity</STRONG></P><P>To begin, create a new Data Definition using the template VIEW &gt; extendView.<BR />[Choose <EM>extendView</EM>&nbsp;if the original data source is a View, or <EM>extendViewEntity</EM>&nbsp;if the original data source is a viewEntity.]</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Pasted image 20240620193148.png" style="width: 669px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128594i9D16B9C97EB89E56/image-dimensions/669x447?v=v2" width="669" height="447" role="button" title="Pasted image 20240620193148.png" alt="Pasted image 20240620193148.png" /></span></P><P>In our case, we extended <STRONG><FONT face="courier new,courier">I_QltyNotification</FONT></STRONG>&nbsp;first as it was the initial CDS view from the top (note that associations cannot be used in an extendViewEntity).</P><pre class="lia-code-sample language-abap"><code>extend view I_QltyNotification with &lt;YOUR EXTEND VIEW NAME&gt; association [1..1] to I_NotificationPriorityText as _NotificationPriority1 on _NotificationPriority1.NotificationPriority = $projection.notificationpriority and _NotificationPriority1.NotificationPriorityType = $projection.notificationprioritytype and _NotificationPriority1.Language = $projection.masterlanguage { _NotificationPriority1.NotificationPriorityText }</code></pre><P><STRONG>Important Notes:</STRONG></P><P>- Always choose the first CDS view from the top to minimize the number of extended views needed.<BR />- Ensure all key fields are included in the association's ON condition. For <FONT face="courier new,courier" color="#333333"><STRONG>I_NotificationPriorityText</STRONG></FONT>, this includes <STRONG><FONT face="courier new,courier">NotificationPriority</FONT></STRONG>, <STRONG><FONT face="courier new,courier">NotificationPriorityType</FONT></STRONG>, and <STRONG><FONT face="courier new,courier">Language</FONT></STRONG>.</P><P>Next, proceed step by step, creating one extendView / extendViewEntity at a time and adding your extended field. For our example, after extending <STRONG><FONT face="courier new,courier">I_QltyNotification</FONT></STRONG>, we extended <STRONG><FONT face="courier new,courier">R_QltyNotificationTP</FONT></STRONG> (viewEntity) next.</P><pre class="lia-code-sample language-abap"><code>extend view entity R_QltyNotificationTP with { Notification.NotificationPriorityText }</code></pre><P>&nbsp;Finally, extend <FONT face="courier new,courier"><STRONG>A_QltyNotification</STRONG></FONT> (CDS View Entity), which is already exposed in the <FONT face="courier new,courier"><STRONG>API_QualityNotification</STRONG></FONT> Service Definition.</P><pre class="lia-code-sample language-abap"><code>extend view entity A_QltyNotification with { _QltyNotification.NotificationPriorityText }</code></pre><P><STRONG>Conclusion</STRONG></P><P>You have successfully extended the standard API. The newly added field can now be seen in the standard API response.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="DeepakMalik_0-1719331078325.png" style="width: 779px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128602iB86A19E789B37885/image-dimensions/779x402?v=v2" width="779" height="402" role="button" title="DeepakMalik_0-1719331078325.png" alt="DeepakMalik_0-1719331078325.png" /></span></P><P><EM>Feel free to reach out if you have any questions or need further clarification.</EM></P> 2024-06-26T18:13:09.567000+02:00 https://community.sap.com/t5/technology-blogs-by-members/abap-restful-application-programming-model-rap/ba-p/13743339 ABAP RESTful Application Programming Model (RAP) 2024-06-28T16:07:38.695000+02:00 Rijul_VN https://community.sap.com/t5/user/viewprofilepage/user-id/1457629 <P><STRONG><U>Introduction</U></STRONG></P><P>The SAP landscape has evolved significantly, with businesses seeking simpler, more efficient solutions that offer excellent user experiences. Many organizations remain deeply embedded in the SAP ecosystem, primarily focusing on ABAP over other languages. So, is it possible to develop feature-rich applications without other frontend languages? Yes, leveraging ABAP with RAP (ABAP Restful Application Programming) makes it possible.</P><P>Restful Application Programming is an ABAP programming model for creating business applications and services in an AS ABAP or BTP ABAP environment. RAP offers a standardized way of developing applications using Core Data Services (CDS), the modernized extended ABAP language, OData protocol, and the concept of business objects and services. RAP applications can only be created through ABAP development tools (ADT) and it’s available in SAP BTP ABAP Environment, SAP S/4 HANA Cloud, and AS ABAP &gt;=7.56.</P><P>Before digging deeper into RAP, let’s explore CDS, annotations, and business services. To illustrate these concepts, let’s create a simple read-only list report application.</P><P><STRONG><U>Developing an OData Service for simple list reporting</U></STRONG></P><P>An OData service follows the best practices for developing and consuming RESTful APIs. This service can be used in SAP Fiori applications and can also be exposed as Web APIs. Below are the steps for creating a simple list report application:</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Rijul_Haridasan_0-1719405864152.png" style="width: 568px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128959i0D0B7F884BD841E5/image-dimensions/568x199?v=v2" width="568" height="199" role="button" title="Rijul_Haridasan_0-1719405864152.png" alt="Rijul_Haridasan_0-1719405864152.png" /></span></P><P>Let’s explore each step in detail by creating the application.</P><P>Sample requirement: Create a read-only list report application which shows purchase order information.</P><UL><LI>Create an interface CDS view which takes data from Purchase Order Header (EKKO) and Item (EKPO).</LI></UL><P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_2-1719405962814.png" style="width: 469px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128963i723AA4D2D8A8EEC6/image-dimensions/469x156?v=v2" width="469" height="156" role="button" title="Rijul_Haridasan_2-1719405962814.png" alt="Rijul_Haridasan_2-1719405962814.png" /></span></P><P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_5-1719406138616.png" style="width: 483px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128967iC713BC2798FA1AEC/image-dimensions/483x250?v=v2" width="483" height="250" role="button" title="Rijul_Haridasan_5-1719406138616.png" alt="Rijul_Haridasan_5-1719406138616.png" /></span></P><P>&nbsp; &nbsp; &nbsp; &nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_6-1719406201461.png" style="width: 480px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128968iF82725627F783085/image-dimensions/480x186?v=v2" width="480" height="186" role="button" title="Rijul_Haridasan_6-1719406201461.png" alt="Rijul_Haridasan_6-1719406201461.png" /></span></P><P>&nbsp;&nbsp;</P><UL><LI>Create two interface CDS views for showing master data of purchase order type and material details.</LI></UL><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_7-1719406268505.png" style="width: 479px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128969iA2BC3F4D3689D062/image-dimensions/479x219?v=v2" width="479" height="219" role="button" title="Rijul_Haridasan_7-1719406268505.png" alt="Rijul_Haridasan_7-1719406268505.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_8-1719406312400.png" style="width: 476px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128970i4188CFAC4C729F7A/image-dimensions/476x207?v=v2" width="476" height="207" role="button" title="Rijul_Haridasan_8-1719406312400.png" alt="Rijul_Haridasan_8-1719406312400.png" /></span></P><DIV class="">&nbsp;</DIV><P>&nbsp;</P><UL><LI>Make an association between the purchase order type CDS view and material details CDS view from the purchase order header/item CDS view. The associated views will act as Search Help in the list report after applying the annotations.<span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_9-1719406407548.png" style="width: 521px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128971i1A435154E00D5269/image-dimensions/521x250?v=v2" width="521" height="250" role="button" title="Rijul_Haridasan_9-1719406407548.png" alt="Rijul_Haridasan_9-1719406407548.png" /></span><P>&nbsp;</P><UL><LI>Create a consumption view on top of the Purchase Order Header/Item interface view (<STRONG>ZI_PURCHASE_ORDER_RVN</STRONG>).</LI></UL><P>The UI annotations needed for the application are written in the consumption CDS View or Metadata Extensions.</P><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_10-1719406543553.png" style="width: 474px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128973i36191613A1DC5757/image-dimensions/474x270?v=v2" width="474" height="270" role="button" title="Rijul_Haridasan_10-1719406543553.png" alt="Rijul_Haridasan_10-1719406543553.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_11-1719406620405.png" style="width: 473px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128974iC736122CB48DEA97/image-dimensions/473x115?v=v2" width="473" height="115" role="button" title="Rijul_Haridasan_11-1719406620405.png" alt="Rijul_Haridasan_11-1719406620405.png" /></span><P>&nbsp;</P><P>Now, we have the data model and the required annotations to manifest semantics for it. The next step is to create the OData service and binding the service.</P><P>To define a service, we first need to create a service definition. In service definition, we specify the CDS entities that need to be exposed. In this example, the gateway client is replaced by the service definition and service binding.</P><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_13-1719406801073.png" style="width: 500px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128977iC1F52E86F3513638/image-dimensions/500x244?v=v2" width="500" height="244" role="button" title="Rijul_Haridasan_13-1719406801073.png" alt="Rijul_Haridasan_13-1719406801073.png" /></span><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_14-1719406869451.png" style="width: 501px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128978iAC5327643E120712/image-dimensions/501x262?v=v2" width="501" height="262" role="button" title="Rijul_Haridasan_14-1719406869451.png" alt="Rijul_Haridasan_14-1719406869451.png" /></span><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_1-1719407201734.png" style="width: 498px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128984i973606333B8DF392/image-dimensions/498x96?v=v2" width="498" height="96" role="button" title="Rijul_Haridasan_1-1719407201734.png" alt="Rijul_Haridasan_1-1719407201734.png" /></span><P>As a last step, create the service binding for service definition.</P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_2-1719407258464.png" style="width: 500px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128985i1E9B8BB0FC5542D6/image-dimensions/500x191?v=v2" width="500" height="191" role="button" title="Rijul_Haridasan_2-1719407258464.png" alt="Rijul_Haridasan_2-1719407258464.png" /></span><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_4-1719407352780.png" style="width: 497px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128987i0620E0281B5907F6/image-dimensions/497x302?v=v2" width="497" height="302" role="button" title="Rijul_Haridasan_4-1719407352780.png" alt="Rijul_Haridasan_4-1719407352780.png" /></span><P>Set the binding type as OData V2 – UI, since this is an OData V2 service.</P><P>&nbsp;</P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_8-1719407475099.png" style="width: 499px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128991iA07EFAFB74BEF9F8/image-dimensions/499x175?v=v2" width="499" height="175" role="button" title="Rijul_Haridasan_8-1719407475099.png" alt="Rijul_Haridasan_8-1719407475099.png" /></span><P>After publishing the service, the exposed entity and associated entities will be visible. Click on the entity and click the preview button to see the preview of the application.</P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_9-1719407529639.png" style="width: 508px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128992iFD1B3C8B4A9F982A/image-dimensions/508x232?v=v2" width="508" height="232" role="button" title="Rijul_Haridasan_9-1719407529639.png" alt="Rijul_Haridasan_9-1719407529639.png" /></span><P>&nbsp;</P><P>Purchasing Doc Type Search Help</P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_10-1719407587236.png" style="width: 489px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128993iB4FFF6C23B36146B/image-dimensions/489x298?v=v2" width="489" height="298" role="button" title="Rijul_Haridasan_10-1719407587236.png" alt="Rijul_Haridasan_10-1719407587236.png" /></span><P>&nbsp;</P></LI></UL><P>&nbsp; &nbsp; &nbsp; &nbsp; Material Search Help</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_11-1719407636266.png" style="width: 431px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128994i9F9D6E0E816D6BCC/image-dimensions/431x265?v=v2" width="431" height="265" role="button" title="Rijul_Haridasan_11-1719407636266.png" alt="Rijul_Haridasan_11-1719407636266.png" /></span></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Rijul_Haridasan_12-1719407740867.png" style="width: 517px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/128995i45168752B35931F6/image-dimensions/517x225?v=v2" width="517" height="225" role="button" title="Rijul_Haridasan_12-1719407740867.png" alt="Rijul_Haridasan_12-1719407740867.png" /></span></P><P>&nbsp;</P><P><STRONG><U>Conclusion</U></STRONG></P><P>This blog serves as an introduction to developing OData services for simple list reporting using the ABAP Restful Application Programming (RAP) model. By following the steps outlined, you can create a read-only list report application that showcases purchase order information. We have covered the basics of creating CDS views, defining and binding OData services, and incorporating annotations for enhanced functionality.</P><P>This is just the beginning of what you can achieve with RAP. As you explore further, you will discover more advanced features and capabilities that can help you build robust, scalable, and user-friendly applications within the SAP ecosystem.</P><P>Happy Learning and exploring the vast possibilities with RAP!</P><P>&nbsp;</P><P><EM>References</EM></P><P><A href="https://help.sap.com/docs/abap-cloud/abap-rap/abap-restful-application-programming-model" target="_blank" rel="noopener noreferrer"><EM>https://help.sap.com/docs/abap-cloud/abap-rap/abap-restful-application-programming-model</EM></A></P><P><A href="https://help.sap.com/docs/abap-cloud/abap-rap/cds-annotations" target="_blank" rel="noopener noreferrer"><EM>https://help.sap.com/docs/abap-cloud/abap-rap/cds-annotations</EM></A></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2024-06-28T16:07:38.695000+02:00 https://community.sap.com/t5/technology-blogs-by-members/sap-rap-unmanaged-scenario-example-simplified/ba-p/13750851 SAP RAP Unmanaged scenario example-Simplified 2024-07-04T06:47:06.640000+02:00 anjan_paul https://community.sap.com/t5/user/viewprofilepage/user-id/183963 <P>SAP RAP (ABAP RESTful Application Programming Model) has two main flavors: managed and unmanaged. Let's focus on the unmanaged version.</P><P><STRONG>Unmanaged SAP RAP</STRONG> refers to a development approach where developers have more control over the data persistence and business logic compared to the managed approach. Here are some key aspects:</P><OL><LI><STRONG>Custom Logic</STRONG>: In unmanaged RAP, developers write their own custom logic for handling data retrieval, manipulation, and persistence. This gives more flexibility in how data is processed and stored.</LI><LI><STRONG>Direct Database Access</STRONG>: Developers can directly access the database tables and define their own data models using Core Data Services (CDS) views or ABAP classes.</LI><LI><STRONG>Explicit Service Definition</STRONG>: Unlike managed RAP, where service definitions are automatically generated based on annotations, unmanaged RAP requires developers to explicitly define service implementations and behaviors.</LI><LI><STRONG>Manual CRUD Operations</STRONG>: CRUD (Create, Read, Update, Delete) operations need to be implemented explicitly in unmanaged RAP, giving full control over how data is managed.</LI><LI><STRONG>Integration with Existing Systems</STRONG>: Unmanaged RAP is often used when integrating with existing systems or when there is a need for complex business logic that cannot be easily handled by the managed approach.</LI><LI><STRONG>Flexibility</STRONG>: Developers have more freedom to implement complex validation rules, authorization checks, and other custom requirements directly in the application logic.</LI></OL><P>Overall, unmanaged SAP RAP provides a more hands-on approach to application development compared to the managed approach, allowing developers to leverage their expertise in ABAP programming and database handling while building modern RESTful APIs.</P><P>Top of Form</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_0-1720068224622.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132111iAE2F232B2E3806F1/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_0-1720068224622.png" alt="anjan_paul_0-1720068224622.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>In this example, we will show a simple application for Employee build with RAP Unmanaged flavors.</P><P>&nbsp;</P><P><STRONG>Development steps.</STRONG></P><P><STRONG>&nbsp;</STRONG></P><P>To be summarized below object will be created for Unmanaged scenario.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_1-1720068224631.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132113i3B10364063FA0353/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_1-1720068224631.png" alt="anjan_paul_1-1720068224631.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>Table ZT01_EMPLOYEE</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_2-1720068224632.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132112i7265BD4DA2C5AE8A/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_2-1720068224632.png" alt="anjan_paul_2-1720068224632.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>Base CDS View Z_I_EMPLOYEES_U</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_3-1720068224633.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132114iE7477DA8A7679A37/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_3-1720068224633.png" alt="anjan_paul_3-1720068224633.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>Consumption CDS view&nbsp; Z_C_EMPLOYEES_U</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_4-1720068224639.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132116i1B730225181C4DA0/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_4-1720068224639.png" alt="anjan_paul_4-1720068224639.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>Behavior Definition</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_5-1720068224640.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132115i8D2CCE6B378E2B7B/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_5-1720068224640.png" alt="anjan_paul_5-1720068224640.png" /></span></P><P>&nbsp;</P><P>Bottom of Form</P><P>&nbsp;</P><P>Behavior &nbsp;definition</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_6-1720068224641.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132117i88DF6633A7BD9646/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_6-1720068224641.png" alt="anjan_paul_6-1720068224641.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>Implement the Create &nbsp;method</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_7-1720068224644.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132118i496873D110842915/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_7-1720068224644.png" alt="anjan_paul_7-1720068224644.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>Implement Update Method</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_8-1720068224646.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132119iCFB1D0ED90CC7D2D/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_8-1720068224646.png" alt="anjan_paul_8-1720068224646.png" /></span></P><P>&nbsp;</P><P>Implement Delete Method</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_9-1720068224649.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132120iB198EFC7938AC0CB/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_9-1720068224649.png" alt="anjan_paul_9-1720068224649.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>Implement Adjust_Numbers method.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_10-1720068224652.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132121i02C0D5C73644F02B/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_10-1720068224652.png" alt="anjan_paul_10-1720068224652.png" /></span></P><P>&nbsp;</P><P>Implement Save method.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_11-1720068224652.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132122i5F986B13096A60EB/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_11-1720068224652.png" alt="anjan_paul_11-1720068224652.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P><STRONG>Test</STRONG></P><OL><LI>Open the Application.</LI></OL><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_12-1720068224655.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132123iF959FD76C9F0B9AB/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_12-1720068224655.png" alt="anjan_paul_12-1720068224655.png" /></span></P><P>&nbsp;</P><P>&nbsp;2. Click on Create. Give Input value and Create.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_13-1720068224657.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132124iEB7481CFE521F5F6/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_13-1720068224657.png" alt="anjan_paul_13-1720068224657.png" /></span></P><P>&nbsp;</P><P>3. New Record got created.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_14-1720068224660.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132125iBFB1122B7011EAAA/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_14-1720068224660.png" alt="anjan_paul_14-1720068224660.png" /></span></P><P>&nbsp;</P><P>4. Select any Row , click on Edit.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_15-1720068224662.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132128i65B2C34F772678D5/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_15-1720068224662.png" alt="anjan_paul_15-1720068224662.png" /></span></P><P>&nbsp;</P><P>5. Change the value and Save.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_16-1720068224664.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132126i20DD29BF54520B47/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_16-1720068224664.png" alt="anjan_paul_16-1720068224664.png" /></span></P><P>&nbsp;</P><P>6. Record will be updated.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_17-1720068224666.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132127iAF6ACD49F54239EC/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_17-1720068224666.png" alt="anjan_paul_17-1720068224666.png" /></span></P><P>7. Select the Rows and click on Delete.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_18-1720068224668.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132132iE847FEB87E344172/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_18-1720068224668.png" alt="anjan_paul_18-1720068224668.png" /></span></P><P>&nbsp;</P><P>8. Records will be deleted.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_19-1720068224670.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132129i50FCD9C2243ABA95/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_19-1720068224670.png" alt="anjan_paul_19-1720068224670.png" /></span></P><P>&nbsp;</P><P>9. In the Database table also you can see the records.</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="anjan_paul_20-1720068224671.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/132131i29BF3E51ED338C70/image-size/medium?v=v2&amp;px=400" role="button" title="anjan_paul_20-1720068224671.png" alt="anjan_paul_20-1720068224671.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>So, all the &nbsp;CRUD operation is successful using RAP Unmanaged flavors.</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2024-07-04T06:47:06.640000+02:00 https://community.sap.com/t5/technology-blogs-by-members/using-multivaluefield-with-rap-odatav4/ba-p/13758019 Using Multivaluefield with RAP ODATAV4 2024-07-12T15:38:36.592000+02:00 hazalbeyazal https://community.sap.com/t5/user/viewprofilepage/user-id/890750 <P><SPAN>INTRODUCTION<BR /></SPAN>&nbsp; &nbsp;Although the multivaluefield component in RAP ODATA v4 is technically similar to the multiinput component we used before, it is structurally different and a little more complex to use. In ODATA v4 structure, if we need multiple value input, we need to use multivaluefield. I will simplify the use of multivaluefield, which has a more complex structure compared to other components, and explain it in detail in this blog post.</P><P>&nbsp;&nbsp;Basically, if we need a multivaluefield in an application, the structure in the cds should be create in the most correct way.</P><P>&nbsp; It is necessary to create a structure in which the asset to be given as a context is connected with a 1-N relationship.</P><P><FONT color="#FF9900">&nbsp;<EM> &nbsp;!! This relationship must be added in projection view.</EM></FONT></P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Main CDS Label' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define root view entity main_cds_name as select from source_table as Table composition [1..*] of child_entity (CDS view) as _ChildEntity { key key1 as Key, //other properties //Child _ChildEntity }</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp; On the UI side, since the multivaluefield component will be linked to a field in the child entity, a valuehelp annotation must be added to the corresponding field in this child entity.</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'Child entity cds label' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define view entity child_entity as select from source_table_for_child as _ChildEntity association [0..1] to valuehelp as _ValueHelp on $projection.header = _ ValueHelp.header { //other properties @Consumption.valueHelpDefinition: [{ entity: { name: 'valuehelp', element: 'HeaderIdConcat' } }] header_id as HeaderId, _ ValueHelp.HeaderId as HeaderIdText, //association _ ValueHelp, }</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp; &nbsp;After this structure is created with Restful ABAP and the ODATAV4 service is published, we can use the multivaluefield component on the ui side.</P><P>&nbsp; &nbsp;A custom page application is created with new project from template using BAS.</P><P>1.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="hazalbeyazal_0-1720694928401.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/135386i153E418B38B23BDB/image-size/medium?v=v2&amp;px=400" role="button" title="hazalbeyazal_0-1720694928401.png" alt="hazalbeyazal_0-1720694928401.png" /></span></P><P>2.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="hazalbeyazal_1-1720694946560.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/135387iE92FE3680F0AAA6D/image-size/medium?v=v2&amp;px=400" role="button" title="hazalbeyazal_1-1720694946560.png" alt="hazalbeyazal_1-1720694946560.png" /></span></P><P>&nbsp;</P><P>3.</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-markup"><code> &lt;macros:MultiValueField id="idMultivalufield" metaPath="_ChildEntity/HeaderId" /&gt;</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>The view will be as follows.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="hazalbeyazal_2-1720695119589.png" style="width: 763px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/135388i7111FE429A59D3C2/image-dimensions/763x82?v=v2" width="763" height="82" role="button" title="hazalbeyazal_2-1720695119589.png" alt="hazalbeyazal_2-1720695119589.png" /></span></P><P>and&nbsp; Valuehelp appear like follows.</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Adsız.png" style="width: 679px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/135391i201F4C8A51465BD0/image-dimensions/679x438?v=v2" width="679" height="438" role="button" title="Adsız.png" alt="Adsız.png" /></span></P><P> <FONT color="#FF9900">Note: If there is a custom workflow and the draft structure is not used, the MultiValueField component may come in "display" mode as the edit mode when the create property is not provided. In these cases, you can activate the edit mode of the component from the controller with the setEditMode("Editable") function.</FONT></P><P>  <FONT color="#FF9900">Note: If multivalufield valuhelp is to be displayed as a dropdown and not as a dialog, the following structure can be added to the annotation.</FONT></P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-markup"><code> &lt;Annotations Target="SAP__self.childEntity/HeaderId"&gt; &lt;Annotation Term="SAP__common.ValueListWithFixedValues" Bool="false"&gt; &lt;/Annotation&gt; &lt;/Annotations&gt;</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P> 2024-07-12T15:38:36.592000+02:00 https://community.sap.com/t5/technology-blogs-by-members/sap-rap-trigger-workflow-events-upon-save-action-in-rap-applications/ba-p/13758168 SAP RAP - Trigger workflow events upon save action in RAP Applications 2024-07-16T18:39:36.729000+02:00 AbdelrahmanZaki https://community.sap.com/t5/user/viewprofilepage/user-id/175819 <P>Hello SAP Community,</P><P>In this post, I will walk you through a technical solution for triggering workflow events from the save action of a Restful ABAP Programming (RAP) application. We aim to assign a buyer to each Purchase Requisition and send a workflow confirmation dialog to the buyer's inbox for necessary actions, such as creating a purchase order.</P><P>The main idea here is to utilize the unmanaged save in the behavior definition to call a function module in the background.</P><P>Using a managed save, the 'save' implementation method restricts you from calling functions, performing rollbacks, commits, or using classic ABAP statements. In contrast, an unmanaged save provides the 'save_modified' method, which allows you to call a function module in a background task and addresses the limitations of classic ABAP statements.</P><P>For more information, you can refer to <A title="behavior_illegal_statement in ABAP RAP" href="https://community.sap.com/t5/technology-q-a/behavior-illegal-statement-in-abap-rap/qaq-p/12688787" target="_self">this Q&amp;A thread</A>.&nbsp;</P><P>&nbsp;</P><P>Let's dive into the implementation details.</P><H2 id="toc-hId-1019483611">Scenario</H2><P>We need to assign a buyer to each Purchase Requisition. Once the buyer is assigned, a workflow confirmation dialog is sent to their inbox, prompting them to take necessary actions, such as creating a purchase order.</P><H2 id="toc-hId-822970106">Solution Outline</H2><P>To achieve this, we'll use an unmanaged save in the behavior definition to call a function module in the background. Here's a step-by-step guide on how to implement this solution.</P><P><STRONG>Note: </STRONG>I will include my complete solution. You might notice several options in the behavior definition. such as:</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>authorization master (instance); update (features: instance, precheck);</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>These instance options enable you to check for edit authority (i.e., who has the edit role).</P><P>Additionally, the validation ensures that the Buyer field is validated upon the save action.</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>validation validateBuyer on save { field Buyer; create; update; } </code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P><STRONG>1. Apend the field Buyer which is from type same as system user (char12)</STRONG></P><P>Note: I prefer to create a new z table without appending the field to standard table EBAN, but I used this case because I was searching for a way to add this editable field in standard screen of Purchase requisition&nbsp;ME53N .. anyway lets complete the structure ZEBAN is append structure for standard table EBAN</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_0-1720699505655.png" style="width: 633px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/135409iE529731E55B619F5/image-dimensions/633x223?v=v2" width="633" height="223" role="button" title="AbdelrahmanZaki_0-1720699505655.png" alt="AbdelrahmanZaki_0-1720699505655.png" /></span></P><P>2. Implement the RAP Application Logic:</P><OL><LI><P><STRONG>Create CDS View: we will use standard cds&nbsp;<SPAN>I_PurchaseReqnItem&nbsp;as reference because our app is most like the standard fiori app&nbsp;<SPAN>Process Purchase Requisitions (<SPAN>App ID&nbsp;<SPAN class="">F1048)</SPAN></SPAN></SPAN></SPAN></STRONG></P><P>&nbsp;</P></LI></OL><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@EndUserText.label: 'PR Buyer Assign' @Metadata.allowExtensions: true @ObjectModel.modelCategory: #BUSINESS_OBJECT @ObjectModel.compositionRoot: true @ObjectModel.transactionalProcessingEnabled: true @ObjectModel.writeActivePersistence: 'ZMM_PR_BUYER_BO' @ObjectModel:{ createEnabled: true, updateEnabled: true, deleteEnabled: true } @Search.searchable: true define root view entity ZCDS_PR_Buyer_Assign as select from I_PurchaseReqnItem as header inner join eban on eban.banfn = header.PurchaseRequisition and eban.bnfpo = header.PurchaseRequisitionItem association [0..1] to I_PurchasingGroup as _PurchasingGroup on $projection.PurchasingGroup = _PurchasingGroup.PurchasingGroup association [0..1] to pa0001 as pa on $projection.RequisitionerName = pa.pernr { //@Consumption.valueHelpDefinition: [{entity:{name:'C_PurchaseReqnHeader' , element: 'PurchaseRequisition'}}] @Consumption.valueHelpDefinition: [{entity:{name:'ZCDS_PR_SEARCH_VALUE' , element: 'PurchaseRequisition'}}] @Consumption.semanticObject: 'PurchaseRequisition' //semanticObjectAction : 'displayAdvanced' key header.PurchaseRequisition, @ObjectModel.readOnly: true header.PurchaseRequisitionItem, @Consumption.valueHelpDefinition: [{entity:{name:'/BSNAGT/P_USR21' , element: 'bname'}}] @ObjectModel.readOnly: false eban.buyer as Buyer, @ObjectModel.readOnly: false eban.comments as Comments, @ObjectModel.readOnly: true header.PurchaseRequisitionType, @Consumption.filter.hidden:true @ObjectModel.readOnly: true @ObjectModel.virtualElement : true @ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_PURCHASEREQN_PROF_TRA_EXT' // cast('' as batxt) as PurchasingDocumentTypeName, cast('' as abap.char( 20 )) as PurchasingDocumentTypeName, @ObjectModel.readOnly: true @Consumption.semanticObject: 'Supplier' header.Supplier, @ObjectModel.virtualElement : true @ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_PURCHASEREQN_PROF_TRA_EXT' @ObjectModel.readOnly: true @EndUserText.label: 'Number of Items' cast(0 as abap.int8 ) as NumberOfItems, header.PurchasingDocumentSubtype, @EndUserText.label: 'Total Value' @Semantics.amount: {currencyCode: 'Currency'} @ObjectModel.readOnly: true @ObjectModel.virtualElement : true @ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_PURCHASEREQN_PROF_TRA_EXT' cast ( ('00') as abap.curr(15,2) ) as TotalNetAmount, @EndUserText.label: 'Currency' @ObjectModel.readOnly: true @ObjectModel.virtualElement : true @ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_PURCHASEREQN_PROF_TRA_EXT' cast ( ('') as abap.cuky( 5 ) ) as Currency, @ObjectModel.readOnly: true header.Material, @ObjectModel.readOnly: true header.PurchaseRequisitionItemText, //@Semantics.businessDate.at:true //header.CreationDate, //this is BADAT field originally @ObjectModel.readOnly: true eban.creationdate as CreationDate, @ObjectModel.readOnly: true header.Plant, @ObjectModel.readOnly: true header.PurReqnReleaseStatus, @ObjectModel.readOnly: true header.PurchasingGroup, @ObjectModel.readOnly: true _PurchasingGroup.PurchasingGroupName as PurchasingGroupName, @ObjectModel.readOnly: true header.ProcessingStatus, @ObjectModel.readOnly: true header.RequisitionerName, //pa0001.ename as RequisitionerNameFull @ObjectModel.readOnly: true header.CreatedByUser, @ObjectModel.readOnly: true pa.ename as CreatedByFullName } where header.PurchaseRequisitionItem = '00010' and header.ProcessingStatus = 'N' and header.PurReqnReleaseStatus = '05' and header.PurchaseRequisitionType &lt;&gt; 'NB'</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P style=" padding-left : 30px; "><BR /><STRONG>2.class for virtual elemets&nbsp;ZCL_PURCHASEREQN_PROF_TRA_EXT</STRONG></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>class ZCL_PURCHASEREQN_PROF_TRA_EXT definition public final create public . public section. interfaces IF_SADL_EXIT . interfaces IF_SADL_EXIT_CALC_ELEMENT_READ . protected section. private section. data MO_PURCHASEREQN_API type ref to CL_PURCHASEREQN_PROF_BOPF_API . ENDCLASS. CLASS ZCL_PURCHASEREQN_PROF_TRA_EXT IMPLEMENTATION. METHOD IF_SADL_EXIT_CALC_ELEMENT_READ~CALCULATE. DATA lt_calculated_purchasereqn TYPE tt_cpurreqn. DATA lt_calculated_purchasereqn2 TYPE table of ZCDS_PR_Buyer_Assign. DATA(lo_purchasereqn_api) = cl_purchasereqn_prof_bopf_api=&gt;get_instance( ). "proceed only if atleast one row of data was read IF it_original_data IS NOT INITIAL. "only if atleast one of the transient fields are requested IF line_exists( it_requested_calc_elements[ table_line = 'PURREQNHEADERDESCRIPTION' ] ) OR line_exists( it_requested_calc_elements[ table_line = 'TOTALNETAMOUNT' ] ) OR line_exists( it_requested_calc_elements[ table_line = 'CURRENCY' ] ) "Added in 1908 Dev code as a part of refactoring OR line_exists( it_requested_calc_elements[ table_line = 'NUMBEROFITEMS' ] ) OR line_exists( it_requested_calc_elements[ table_line = 'PURCHASINGDOCUMENTTYPENAME' ] ) . ***************performance improvement code************************* CALL METHOD lo_purchasereqn_api-&gt;calc_header_transient_fields EXPORTING it_original_data = it_original_data IMPORTING et_purchasereqn_header = lt_calculated_purchasereqn. ***************performance improvement code************************* MOVE-CORRESPONDING lt_calculated_purchasereqn TO lt_calculated_purchasereqn2. MOVE-CORRESPONDING lt_calculated_purchasereqn2 TO ct_calculated_data. ENDIF. ENDIF. ENDMETHOD. METHOD IF_SADL_EXIT_CALC_ELEMENT_READ~GET_CALCULATION_INFO. ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P style=" padding-left : 30px; "><BR />As you notice it is also copy from the standard one for virtual elements.<BR /><BR /><STRONG>3.Create Metadata Extension:<BR /></STRONG>Use the annotation in main CDS @Metadata.allowExtensions: true to allow extensions.<BR /><BR />This extension manages the object page in fiori element app.</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>@Metadata.layer: #PARTNER @Search.searchable: true annotate view ZCDS_PR_Buyer_Assign with { @Search: {defaultSearchElement: true, ranking: #MEDIUM, fuzzinessThreshold: 0.8} : { lineItem: [ { position: 10, importance: #HIGH } ], selectionField: [ { position: 10 } ], identification:[ { position: 10 } ] } PurchaseRequisition; : { lineItem: [ { position: 20, importance: #HIGH } ], selectionField: [ { position: 20 } ], identification:[ { position: 20 } ] } Buyer; : { lineItem: [ { position: 30, importance: #HIGH } ], // selectionField: [ { position: 30 } ], identification:[ { position: 30 } ] //multiLineText: true } Comments; }</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P style=" padding-left : 30px; "><STRONG>4.Behavior Definition (Unmanaged Save):</STRONG></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>managed implementation in class zbp_cds_pr_buyer_assign unique; define behavior for ZCDS_PR_Buyer_Assign lock master authorization master ( instance ) with unmanaged save { update ( features : instance, precheck ); delete ( features : instance, precheck ); field ( mandatory : create, readonly : update ) PurchaseRequisition; field ( mandatory : create ) buyer; field (readonly) PurchaseRequisitionItem; field (readonly) PurchaseRequisitionType; field (readonly) PurchasingDocumentTypeName; field (readonly) NumberOfItems; field (readonly) PurchasingDocumentSubtype; field (readonly) Currency; field (readonly) TotalNetAmount; field (readonly) Material; field (readonly) PurchaseRequisitionItemText; field (readonly) Plant; field (readonly) PurReqnReleaseStatus; field (readonly) PurchasingGroup; field (readonly) PurchasingGroupName; field (readonly) RequisitionerName; field (readonly) CreatedByUser; field (readonly) CreatedByFullName; validation validateBuyer on save { field Buyer; create;update; } }</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P style=" padding-left : 30px; "><STRONG>5.Implement Behavior Class:</STRONG></P><P style=" padding-left : 30px; ">&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>CLASS zbp_cds_pr_buyer_assign DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF zcds_pr_buyer_assign. ENDCLASS. CLASS zbp_cds_pr_buyer_assign IMPLEMENTATION. ENDCLASS.​</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>CLASS lhc_ZCDS_PR_Buyer_Assign DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS validateBuyer FOR VALIDATE ON SAVE IMPORTING keys FOR ZCDS_PR_Buyer_Assign~validateBuyer. METHODS get_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR ZCDS_PR_Buyer_Assign RESULT result. METHODS get_instance_features FOR INSTANCE FEATURES IMPORTING keys REQUEST requested_features FOR ZCDS_PR_Buyer_Assign RESULT result. METHODS is_update_granted RETURNING VALUE(update_granted) TYPE abap_bool. ENDCLASS. CLASS lhc_ZCDS_PR_Buyer_Assign IMPLEMENTATION. METHOD validateBuyer. " Read relevant data READ ENTITIES OF zcds_pr_buyer_assign IN LOCAL MODE ENTITY ZCDS_PR_Buyer_Assign FIELDS ( Buyer ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_buyer_assign) FAILED DATA(lt_failed). failed = CORRESPONDING #( DEEP lt_failed ). DATA lt_buyer TYPE SORTED TABLE OF demo_sales_bupa WITH UNIQUE KEY id. lt_buyer = CORRESPONDING #( lt_buyer_assign MAPPING id = Buyer EXCEPT * ). DATA ls_buyer_assign LIKE LINE OF lt_buyer_assign. DATA ls_buyer LIKE LINE OF lt_buyer. DATA lo_message TYPE REF TO if_abap_behv_message. READ TABLE lt_buyer_assign INTO ls_buyer_assign INDEX 1. READ TABLE lt_buyer INTO ls_buyer INDEX 1. data(has_error) = abap_false. data message_number type numchar3. IF ls_buyer-id IS INITIAL. has_error = abap_true. message_number = 002. ELSE. select single BNAME from usr21 into (lv_BNAME) where BNAME = _buyer-id. if sy-subrc &lt;&gt; 0. has_error = abap_true. message_number = 003. endif. ENDIF. if has_error = abap_true. " If so we are loading a message from the global message class ZCONNECTION_MESSAGES. lo_message = me-&gt;new_message( id = 'ZFIORI' number = message_number "002 severity = ms-error ). " Appending the message to the RAP runtime to show it to the user. APPEND VALUE #( %tky = ls_buyer_assign-%tky %msg = lo_message ) TO reported-zcds_pr_buyer_assign. " Abort the save by adding an entry to the failed component. APPEND VALUE #( %tky = ls_buyer_assign-%tky ) TO failed-zcds_pr_buyer_assign. endif. ENDMETHOD. METHOD get_authorizations. DATA update_grtanted TYPE abap_bool. "update_grtanted = abap_false. DATA lo_message TYPE REF TO if_abap_behv_message. READ ENTITIES OF zcds_pr_buyer_assign IN LOCAL MODE ENTITY ZCDS_PR_Buyer_Assign FIELDS ( Buyer ) WITH CORRESPONDING #( keys ) RESULT DATA(ZCDS_PR_Buyer_Assign_Edit) FAILED failed. CHECK ZCDS_PR_Buyer_Assign_Edit is not initial. data(update_requested) = COND #( WHEN requested_authorizations-%update = if_abap_behv=&gt;mk-on THEN abap_true ELSE abap_false ). data(delete_requested) = COND #( WHEN requested_authorizations-%delete = if_abap_behv=&gt;mk-on THEN abap_true ELSE abap_false ). loop at ZCDS_PR_Buyer_Assign_Edit ASSIGNING FIELD-SYMBOL(&lt;lfs_Buyer_Assign&gt;). if update_requested = abap_true or delete_requested = abap_true. update_grtanted = is_update_granted( ). if update_grtanted = abap_false. lo_message = me-&gt;new_message( id = 'ZFIORI' number = 004 severity = ms-error ). APPEND VALUE #( %tky = &lt;lfs_Buyer_Assign&gt;-%tky ) to failed-ZCDS_PR_Buyer_Assign. APPEND VALUE #( %tky = keys[ 1 ]-%tky %msg = lo_message ) to reported-ZCDS_PR_Buyer_Assign. ENDIF. endif. endloop. ENDMETHOD. METHOD get_instance_features. READ ENTITIES OF zcds_pr_buyer_assign IN LOCAL MODE ENTITY ZCDS_PR_Buyer_Assign ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(entities). data(lv_update_delete) = COND #( WHEN is_update_granted( ) IS INITIAL THEN if_abap_behv=&gt;fc-o-disabled ELSE if_abap_behv=&gt;fc-o-enabled ). result = VALUE #( FOR entity IN entities ( %tky = entity-%tky %update = lv_update_delete %delete = lv_update_delete ) ). ENDMETHOD. METHOD is_update_granted. "For instance auth AUTHORITY-CHECK OBJECT 'ZMM_BUYER' ID 'ACTVT' FIELD '02'. update_granted = COND #( WHEN sy-subrc = 0 THEN abap_true ELSE abap_false ). ENDMETHOD. ENDCLASS. CLASS lsc_ZCDS_PR_Buyer_Assign DEFINITION INHERITING FROM cl_abap_behavior_saver. PROTECTED SECTION. METHODS save_modified REDEFINITION . ENDCLASS. CLASS lsc_ZCDS_PR_Buyer_Assign IMPLEMENTATION. METHOD save_modified. DATA ls_PR_BUYER_BO TYPE zmm_PR_BUYER_BO. DATA lt_PR_BUYER_BO TYPE TABLE OF zmm_PR_BUYER_BO. DATA(trigger_wf) = abap_true. IF create IS NOT INITIAL. "Populate the values lt_PR_BUYER_BO = CORRESPONDING #( create-zcds_pr_buyer_assign ). READ TABLE lt_PR_BUYER_BO INTO ls_PR_BUYER_BO INDEX 1. GET TIME STAMP FIELD ls_PR_BUYER_BO-crea_date_time. ls_PR_BUYER_BO-crea_uname = sy-uname. GET TIME STAMP FIELD ls_PR_BUYER_BO-lchg_date_time. ls_PR_BUYER_BO-lchg_uname = sy-uname. MODIFY zmm_pr_buyer_bo FROM ls_PR_BUYER_BO. UPDATE eban SET buyer = ls_PR_BUYER_BO-buyer comments = ls_PR_BUYER_BO-comments WHERE banfn = ls_PR_BUYER_BO-purchaserequisition AND bnfpo = '00010'. ELSEIF update IS NOT INITIAL. lt_PR_BUYER_BO = CORRESPONDING #( update-zcds_pr_buyer_assign ). READ TABLE lt_PR_BUYER_BO INTO ls_PR_BUYER_BO INDEX 1. GET TIME STAMP FIELD ls_PR_BUYER_BO-lchg_date_time. ls_PR_BUYER_BO-lchg_uname = sy-uname. MODIFY zmm_pr_buyer_bo FROM ls_PR_BUYER_BO. IF ls_PR_BUYER_BO-buyer IS NOT INITIAL. UPDATE eban SET buyer = ls_PR_BUYER_BO-buyer comments = ls_PR_BUYER_BO-comments WHERE banfn = ls_PR_BUYER_BO-purchaserequisition AND bnfpo = '00010'. ELSE. UPDATE eban SET comments = ls_PR_BUYER_BO-comments WHERE banfn = ls_PR_BUYER_BO-purchaserequisition AND bnfpo = '00010'. ENDIF. ELSEIF delete IS NOT INITIAL. trigger_wf = abap_false. lt_PR_BUYER_BO = CORRESPONDING #( delete-zcds_pr_buyer_assign ). READ TABLE lt_PR_BUYER_BO INTO ls_PR_BUYER_BO INDEX 1. GET TIME STAMP FIELD ls_PR_BUYER_BO-lchg_date_time. ls_PR_BUYER_BO-lchg_uname = sy-uname. MODIFY zmm_pr_buyer_bo FROM ls_PR_BUYER_BO. UPDATE eban SET buyer = '' comments = '' WHERE banfn = ls_PR_BUYER_BO-purchaserequisition AND bnfpo = '00010'. ENDIF. IF trigger_wf = abap_true. IF ls_PR_BUYER_BO IS INITIAL. ENDIF. CALL FUNCTION 'ZMM_WF_PR_ASSIGN_NOT' IN BACKGROUND TASK EXPORTING i_pr_buyer = ls_PR_BUYER_BO. ENDIF. ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>6.Create FM&nbsp;<SPAN>zmm_wf_pr_assign_not</SPAN></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>FUNCTION zmm_wf_pr_assign_not IMPORTING VALUE(i_pr_buyer) TYPE zmm_pr_buyer_bo. * Data Declaration for Class DATA lr_wf_pgr TYPE REF TO zmm_wf_pr_assign_notification. DATA ls_pr_buyer TYPE zmm_pr_buyer_bo. CREATE OBJECT lr_wf_pgr. MOVE-CORRESPONDING i_pr_buyer TO ls_pr_buyer. * Trigger Workflow CONCATENATE 'US' ls_pr_buyer-buyer INTO ls_pr_buyer-buyer. lr_wf_pgr-&gt;register_buyer( EXPORTING i_pr_buyer = ls_pr_buyer ). ENDFUNCTION.</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>Table zmm_pr_buyer_bo:</P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_0-1720941239948.png" style="width: 760px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136376i8F3710E28C131683/image-dimensions/760x184?v=v2" width="760" height="184" role="button" title="AbdelrahmanZaki_0-1720941239948.png" alt="AbdelrahmanZaki_0-1720941239948.png" /></span></P><P>&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_0-1720941343822.png" style="width: 769px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136378iC5D687672ECC5D82/image-dimensions/769x313?v=v2" width="769" height="313" role="button" title="AbdelrahmanZaki_0-1720941343822.png" alt="AbdelrahmanZaki_0-1720941343822.png" /></span></P><P>&nbsp;</P><P><STRONG>3. Create Workflow: (Class based workflow)</STRONG></P><P style=" padding-left : 30px; "><STRONG>1. Workflow template</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_1-1720941370881.png" style="width: 782px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136379iB4F3F193621F4634/image-dimensions/782x284?v=v2" width="782" height="284" role="button" title="AbdelrahmanZaki_1-1720941370881.png" alt="AbdelrahmanZaki_1-1720941370881.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_2-1720941384081.png" style="width: 784px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136380iA2F74F185F8A5BAF/image-dimensions/784x212?v=v2" width="784" height="212" role="button" title="AbdelrahmanZaki_2-1720941384081.png" alt="AbdelrahmanZaki_2-1720941384081.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="AbdelrahmanZaki_3-1720941400977.png" style="width: 773px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136381i34CFE46449450BD0/image-dimensions/773x325?v=v2" width="773" height="325" role="button" title="AbdelrahmanZaki_3-1720941400977.png" alt="AbdelrahmanZaki_3-1720941400977.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_4-1720941417448.png" style="width: 704px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136382i5E3946D04A315216/image-dimensions/704x464?v=v2" width="704" height="464" role="button" title="AbdelrahmanZaki_4-1720941417448.png" alt="AbdelrahmanZaki_4-1720941417448.png" /></span></P><P>&nbsp;</P><P style=" padding-left : 30px; "><STRONG>2. Workflow Class</STRONG></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>class ZMM_WF_PR_ASSIGN_NOTIFICATION definition public final create public . public section. interfaces BI_OBJECT . interfaces BI_PERSISTENT . interfaces IF_WORKFLOW . events REGISTER exporting value(I_PR_BUYER) type ZMM_PR_BUYER_BO . methods REGISTER_BUYER importing !I_PR_BUYER type ZMM_PR_BUYER_BO . methods NOTIFICATION importing !I_PR_BUYER type ZMM_PR_BUYER_BO . protected section. private section. ENDCLASS. CLASS ZMM_WF_PR_ASSIGN_NOTIFICATION IMPLEMENTATION. method NOTIFICATION. endmethod. METHOD REGISTER_BUYER. * Data Declarations DATA: lv_objtype TYPE sibftypeid, lv_event TYPE sibfevent, lv_objkey TYPE sibfinstid, lr_event_parameters TYPE REF TO if_swf_ifs_parameter_container. * lv_param_name TYPE swfdname, ** lv_id TYPE char10. * lv_id TYPE zmm_pgr_not_log. * Setting values of Event Name lv_objtype = 'ZMM_WF_PR_ASSIGN_NOTIFICATION'. " Your Class Name lv_event = 'REGISTER'. " Event Name. * Instantiate an empty event container CALL METHOD cl_swf_evt_event=&gt;get_event_container EXPORTING im_objcateg = cl_swf_evt_event=&gt;mc_objcateg_cl im_objtype = lv_objtype im_event = lv_event RECEIVING re_reference = lr_event_parameters. ** Set up the name/value pair to be added to the container * lv_param_name = 'I_PGR_LOG'. " parameter name of the event * lv_id = i_pgr_log. * Add the name/value pair to the event conainer TRY. CALL METHOD lr_event_parameters-&gt;set EXPORTING name = 'I_PR_BUYER' value = I_PR_BUYER. CATCH cx_swf_cnt_cont_access_denied . CATCH cx_swf_cnt_elem_access_denied . CATCH cx_swf_cnt_elem_not_found . CATCH cx_swf_cnt_elem_type_conflict . CATCH cx_swf_cnt_unit_type_conflict . CATCH cx_swf_cnt_elem_def_invalid . CATCH cx_swf_cnt_container . ENDTRY. * Raise the event passing the prepared event container TRY. CALL METHOD cl_swf_evt_event=&gt;raise EXPORTING im_objcateg = cl_swf_evt_event=&gt;mc_objcateg_cl im_objtype = lv_objtype im_event = lv_event im_objkey = lv_objkey im_event_container = lr_event_parameters. CATCH cx_swf_evt_invalid_objtype . CATCH cx_swf_evt_invalid_event . ENDTRY. COMMIT WORK. ENDMETHOD. ENDCLASS.</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>Action the Complete button of this specific task in&nbsp;</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_5-1720937288302.png" style="width: 682px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136362iA289284CAC1576BB/image-dimensions/682x271?v=v2" width="682" height="271" role="button" title="AbdelrahmanZaki_5-1720937288302.png" alt="AbdelrahmanZaki_5-1720937288302.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_4-1720937262313.png" style="width: 622px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136361i72BBBDD4082795D8/image-dimensions/622x159?v=v2" width="622" height="159" role="button" title="AbdelrahmanZaki_4-1720937262313.png" alt="AbdelrahmanZaki_4-1720937262313.png" /></span></P><P>Create Enhancement implementation for badi&nbsp;/IWWRK/ES_WF_WI_BEFORE_UPD_IB</P><P>make sure to map filter values correctly as below</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_6-1720937399206.png" style="width: 689px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136363i6855CACBA81E1C05/image-dimensions/689x165?v=v2" width="689" height="165" role="button" title="AbdelrahmanZaki_6-1720937399206.png" alt="AbdelrahmanZaki_6-1720937399206.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_0-1720942366029.png" style="width: 746px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136385iD0964AB0546D427E/image-dimensions/746x194?v=v2" width="746" height="194" role="button" title="AbdelrahmanZaki_0-1720942366029.png" alt="AbdelrahmanZaki_0-1720942366029.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><pre class="lia-code-sample language-abap"><code>class ZCL_MM_WF_PGR_NOT definition public final create public . public section. interfaces /IWWRK/IF_WF_WI_BEFORE_UPD_IB . interfaces IF_BADI_INTERFACE . protected section. private section. ENDCLASS. CLASS ZCL_MM_WF_PGR_NOT IMPLEMENTATION. method /IWWRK/IF_WF_WI_BEFORE_UPD_IB~BEFORE_UPDATE. * Local Data Declaration DATA: lv_subrc TYPE sy-subrc, lv_status TYPE sww_wistat. CLEAR: lv_subrc, lv_status. IF iv_decision_key EQ '0001'. * Status: Ready to Committed Status CALL FUNCTION 'SAP_WAPI_WORKITEM_COMPLETE' EXPORTING workitem_id = is_wi_details-wi_id IMPORTING return_code = lv_subrc new_status = lv_status. IF lv_subrc EQ 0 AND lv_status NE 'COMPLETED'. CLEAR: lv_status. * Status: Committed to Completed Status CALL FUNCTION 'SAP_WAPI_WORKITEM_COMPLETE' EXPORTING workitem_id = is_wi_details-wi_id IMPORTING return_code = lv_subrc new_status = lv_status. ENDIF. ENDIF. endmethod. ENDCLASS.</code></pre><P>&nbsp;</P><P>&nbsp;</P><P>&nbsp;</P><P><STRONG>4.Publish app to Fiori Launchpad</STRONG></P><P>I went with the minimum option (without&nbsp;SAP Business Application Studio) as follows:</P><P>published the cds view in SEGW project and created a project from template (List Report) in webide then deploy it to server under name&nbsp;zmm_pr_assign<BR /><BR /></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_4-1720940223496.png" style="width: 778px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136374iEE89D7DCA52A948D/image-dimensions/778x390?v=v2" width="778" height="390" role="button" title="AbdelrahmanZaki_4-1720940223496.png" alt="AbdelrahmanZaki_4-1720940223496.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_2-1720939795896.png" style="width: 764px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136372iAF97EB5DD7BA3CD4/image-dimensions/764x292?v=v2" width="764" height="292" role="button" title="AbdelrahmanZaki_2-1720939795896.png" alt="AbdelrahmanZaki_2-1720939795896.png" /></span></P><P>&nbsp;</P><P>&nbsp;</P><P>Target mapping</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_3-1720940086280.png" style="width: 668px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136373i21F5630670AA2573/image-dimensions/668x249?v=v2" width="668" height="249" role="button" title="AbdelrahmanZaki_3-1720940086280.png" alt="AbdelrahmanZaki_3-1720940086280.png" /></span></P><P>I'm sure you will manage to create tile, catalog, group, and pfcg role. <span class="lia-unicode-emoji" title=":slightly_smiling_face:">🙂</span></P><P>&nbsp;</P><P><STRONG>5.Output:</STRONG></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_8-1720937777702.png" style="width: 400px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136365iDC16E799AFD8B8CF/image-size/medium?v=v2&amp;px=400" role="button" title="AbdelrahmanZaki_8-1720937777702.png" alt="AbdelrahmanZaki_8-1720937777702.png" /></span></P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_5-1720942130979.png" style="width: 753px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136383i0711AA6664706F1C/image-dimensions/753x253?v=v2" width="753" height="253" role="button" title="AbdelrahmanZaki_5-1720942130979.png" alt="AbdelrahmanZaki_5-1720942130979.png" /></span><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_6-1720942160666.png" style="width: 744px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136384i5E0507BD39F52B5A/image-dimensions/744x212?v=v2" width="744" height="212" role="button" title="AbdelrahmanZaki_6-1720942160666.png" alt="AbdelrahmanZaki_6-1720942160666.png" /></span></P><P>Once saved here you find wf item in my inbox with binded details of PR.. with the complete button visible</P><P><span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="AbdelrahmanZaki_12-1720938047471.png" style="width: 716px;"><img src="https://community.sap.com/t5/image/serverpage/image-id/136369i9CA7EBE9D771FCB7/image-dimensions/716x342?v=v2" width="716" height="342" role="button" title="AbdelrahmanZaki_12-1720938047471.png" alt="AbdelrahmanZaki_12-1720938047471.png" /></span></P><P>&nbsp;</P><P>I've included all the important details in sequence, but there may still be some missing information.</P><P>Please feel free to ask about any additional details you'd like me to include.</P><P><BR />Regards,</P><P>Abdelrahman Zaki</P><P>&nbsp;</P> 2024-07-16T18:39:36.729000+02:00