Follow SAP ABAP Central on Feedspot

Continue with Google
Continue with Facebook

S/4 HANA Embedded Analytics KPI tile provides drill down capabilities. Clicking on KPI tile takes you to default drill down, where you can continue your analysis apply filters and drill down to the most granular data level e.g document level. Finally you can drill down to application (insight to action) to make transaction changes etc.

Configuring KPI tile drill downs on charts and tables is straight forward. What I will focus on is how to configure drill down to application using navigation intent (insight to action).

There is very little information available on how to setup navigation intent. I found some information in App Library for Configure KPI Drill-down app.

In order to Display Flight link appear in pop-over menu (as in my example above) following needs to be done:

1. Define Target mapping for the App
2. Define Navigation Intent in KPI evaluation

Define Target mapping for Display SFLIGHT records App

Note: In order for link to appear in pop-over following conditions need to be met:

1. User is authorized to Catalog where Target Mapping is saved;

2. App parameters are defined as mandatory (otherwise link will appear in Open In.. button menu which less consistent in terms of interface and adds a couple more clicks during navigation compared to pop-over menu)

Note: it is not a documented functionality, but from my experience Semantic Object Action should be named display, otherwise link will appear in Open In.. button menu

Note: parameters name are the one taken from CDS View

Note: Target Name for SAP GUI transaction is a screen field for Batch Input

Define Navigation Intent in KPI Tile Evaluation

  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Recently, I’ve been checking some examples of unit and currency conversions based on ABAP CDS views and I noticed is quite common to find people applying manual conversions in the formulas.

A simple example of a days to years conversion is conceived with a multiplication of the original value by 365, for more complex situations like an expansion of this same conversion from days to months and years a CASE statement is implemented to adjust the output of the value.

But what happens when you have conversion of an amount to different currencies or a unit conversion based on dynamic values?

In these cases is impossible to apply a fixed calculation in the code due to the variations of the currency or unit, but what most part of the consultants don’t know is that ABAP CDS views provide support to set of functions conceived specifically for this kind of scenario:


But how these functions work?

Unit conversion has 3 mandatory parameters + 2 optional parameters to support client verification and error handling. The specification is available below:

Formal ParameterOptional Data Type 
quantityQUAN, DEC, INT1, INT2, INT4, FLTP
source_unit – UNIT 
target_unit– UNIT 
client X, – CLNT 
error_handling CHAR with length 20

Currency conversion has 4 mandatory parameters + 6 optional parameters to support fine adjustments in the output, client verification and error handling. The specification is available below:

Formal ParameterOptional Data Type 
source_currency – CUKY 
target_currency – CUKY 
exchange_rate_date – DATS 
exchange_rate_type CHAR with length 4 
client X, – CLNT 
round  CHAR 
decimal_shift CHAR 
decimal_shift_back  CHAR 
error_handling CHAR with length 20 

In this short post we are going to create a demo exploring both functions based on two different entities from the Flights demo tables:


In the diagram above you can find the connection between both tables. Notice that SAPLANE holds the data of the planes and SFLIGHT stores data of the flights.

Example #1: Unit Conversion

For the unit conversion we will use one of the fields from SAPLANE table.

There are 3 valid options based on the Currency/Quantity Fields:

For this example, I am using a conversion based on Maximum fuel capacity (TANKCAP). The objective is to demonstrate multiple conversions based on the same dimension.

To check the valid options to convert this field you can open the table T006 and search units based on the Volume dimension.

Checking the data available in the table we can notice TANKCAP is usually referred by Liters (L). Let’s try to create conversions to Cubic Meters (M3) and Cubic Yards (YD3) via this standard function.

The code and delivery are pretty simple, create a new Data Definition via HANA Studio with the name of ZCDS_UNIT_CONVERSION and include the following code.

@AbapCatalog.sqlViewName: 'ZCDSUNITCONV'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'CDS Demo by Felipe Rodrigues'

  as select from saplane
      @EndUserText.label: 'Plane Type'
  key planetype as PlaneType,

      @EndUserText.label: 'Tank Cap'
      tankcap   as TankCapacity,

      @EndUserText.label: 'Tank Cap (UOM)'
      cap_unit  as CapacityUnit,

      @EndUserText.label: 'Tank Cap (in Cubic Meters)'
        quantity    => tankcap,
        source_unit => cap_unit,
        target_unit => cast('M3' as abap.unit)
      )         as TankCapacityInM3,

      @EndUserText.label: 'Tank Cap (in Cubic Yard)'
        quantity    => tankcap,
        source_unit => cap_unit,
        target_unit => cast('YD3' as abap.unit)
      )         as TankCapacityInYD3

Run the Data Preview and check the result:

Let’s re-conciliate the data from the first record via google assistant.

Liters (L) to Cubic Meters (M3)

Liters (L) to Cubic Yards (YD3)

Important Note: Due to a small difference in the number of decimal places in the conversion factor used by SAP and Google you can notice also a small difference between the results, just remember the conversion factor can be adjusted in SAP depending on your needs.

Example #2: Currency Conversion

For the currency conversion we are going to use some of the fields from SFLIGHT table.

For this example, I am using Airfare (PRICE) and converting the value to Australian Dollars (AUD).

This is a pretty interesting scenario because the prices usually vary depending depending on the flight and company, with the standard function we are able to convert the values dynamically.

To achieve this result let’s create a new Data Definition via HANA Studio with the name of ZCDS_CURRENCY_CONVERSION and include the following code.

@AbapCatalog.sqlViewName: 'ZCDSCURRCONV'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'CDS Demo by Felipe Rodrigues'

  as select from sflight
      @EndUserText.label: 'Airline Code'
  key carrid   as AirlineCode,

      @EndUserText.label: 'Connection No.'
  key connid   as ConnectionNumber,

      @EndUserText.label: 'Flight Date'
  key fldate   as FlightDate,

      @EndUserText.label: 'Price'
      price    as Price,

      @EndUserText.label: 'Currency'
      currency as Currency,

      @EndUserText.label: 'Price (in Australian Dollars)'
        amount             => price,
        source_currency    => currency,
        target_currency    => cast('AUD' as abap.cuky),
        exchange_rate_date => fldate
      )        as PriceInAUD

Notice the CURRENCY_CONVERSION has a mandatory parameter named EXCHANGE_RATE_DATE, for this scenario we need to assign the Flight Date for the exchange date, the system is going to analyse the options available in the table TCURR automatically and provide the proper conversion rate based on the date the customer acquired the flight ticket.

Run the Data Preview and check the result below:

Important Note: The accuracy of your currency conversion will depend on how often you update the Exchange Rate in your SAP system. In my case the conversion is not accurate because this data is from a development environment but if you have updated conversion rates in your system you will find the proper values in the output of this CDS view.
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Motivation and Introduction:
Rest Services are very popular and widely used. But Rest is only a programming paradigm and or an architectural approach as such it doesn’t set up any contract how services are offered, and which format the payload will have finally.

To address this contract there are a lot of service descriptions available meanwhile. You can have a look on how many there are at wikipedia. The ones I came accross the last years very frequently are oData and openApi.

SAP Netweaver comes with great support of oData and as both service descriptions have the same paradigm (Rest) underneath the differences between oData and openApi are not that great as one maybe think about.

Although it’s possible to write Restful Services with ABAP with some framework classes of package SREST you won’t have the service infrastructure of SAP Netweaver Gateway at hand and nonfunctional requirements, like technical error logging and tracing capabilities become crucial with distributed applications.

The OASIS group is maintaining a toolset to generate an openApi description from oData service description document.


Here I’d like to dive into that and see how to build a simple java client from such an openApi description which transparently accesses an oData service. That becomes especially interesting with oData v4, as the payload is ‘plain json’ there. So we’ve a good chance to see that working and leverage the SAP Netweaver Gateway features for openApi services built on the ABAP stack.

In order to do that we will:

1. Build and deploy a simple v4 oData service
2. Download and install the toolset to generate the openApi description
3. Generate the openApi description the oData services description
4. Download and build the swagger code generator
5. Generate and install the openapi client from the openApi description
6. Build and run a simple java program which uses the openapi client generated.

To follow the steps here the following software need to be available/installed:

◈ Nodejs
◈ Git SCM
◈ JDK (here 1.8)
◈ Apache Maven
◈ SAP Netweaver ABAP 750 SP4
Build and Deploy a simple v4 oData service

Go to Transaction “SEGW” and click “New” button to create a new service

Create the project with the settings shown at the screenshot. Especially choose Project type “OData 4.0 Service” and store as local object.

Expand the node „Data Model“ double click on Entity Types, press the New button on the right pane, enter Entity Type Name „contractType“ and press enter.

1. Expand the node „contractType“, double click on properties
2. Press the New button two times.
3. Set the first properties name to „contractId“, check the checkbox for „Key“, select type kind to „Core Type“ and choose data type „Edm.String“
4. Set the second properties name to „contractName“, select „type kind to „Core Type“ and choose data type „Edm.String“
5. Press „Save“

Double click on entity sets, press the new button on the right pane, set entity set name to „contract“ and entity type name to „contractType“, press enter and save

Press Generate

Leave the class names as provided and save as local object in the next dialogs.

1. Expand the runtime artifacts
2. Double click on „ZCL_ZTEST_SERVICE_V4_DPC_EXT“
3. And double click on „ZCL_ZTEST_SERVICE_V4_DPC_EXT“ on the right pane in order to open the class editor.

Add the following method to ZCL_ZTEST_SERVICE_V4_DPC_EXT

    clear ET_CONTRACTS.

    <fs_contract>-CONTRACTID = 1.
    <fs_contract>-CONTRACTNAME = 'Sample contract 1'.

    <fs_contract>-CONTRACTID = 2.
    <fs_contract>-CONTRACTNAME = 'Other contract'.

    <fs_contract>-CONTRACTID = 3.
    <fs_contract>-CONTRACTNAME = 'Test contract '.

    <fs_contract>-CONTRACTID = 4.
    <fs_contract>-CONTRACTNAME = 'Special contract'.

    <fs_contract>-CONTRACTID = 5.
    <fs_contract>-CONTRACTNAME = 'Final contract'.


Redefine the method CONTRACT_READ_LIST as follows:

*    IO_REQUEST  =
*    .

      ET_CONTRACTS = data(lt_contracts)
*    catch /IWBEP/CX_GATEWAY.    "

        ls_proc_info type /IWBEP/IF_V4_REQU_BASIC_CREATE=>TY_S_TODO_LIST.



Goto Transaction /IWBEP/V4_ADMIN

Click on Register Group

Create a service group with the following settings:

Service Group: ZTEST_SERVICE_V4

Description: Odata V4 Sample Services

Package: $TMP

Click on register

Register the service as shown in the screenshot

Finally you should see the Service we’ve created assigned to the service group we’ve created.

Goto Transaction /IWFND/V4_ADMIN

Click on “Publish Service Groups”

Choose System alias „LOCAL“ and press button „Get Service Groups“

Select the Service Group „ZTEST_SERVICE_V4“ (the group we’ve created) and press „Publish Service Groups“

Leave the settings as they are at the „Publish Service Group“ dialog and click „Ok“. Then create a new transport request an save the settings.

You should see a success message.

Go back to /IWBEP/V4_ADMIN find the service group and select the service and press the button „Service Test“

Choose local gateway system

Now you see the Gateway Client with the metadata url. Press „Execute“ to retrieve the services metadata.

Now we’ve the oData Service description document. Right click in the browser and save it as „odata_v4_service_description.xml“

Additionally we can do some testing. Use the url „/sap/opu/odata4/sap/ztest_service_v4/default/sap/ztest_v4_service/0001/contract“ and Execute

So we see the sample data we’ve provided at the mock data method.

For now, we’ve our oData v4 Test Service and the according service description document. So we can go on to translate the oData Service description to a openApi description.

Download and install the toolset to generate the openApi description

Here we will install the toolset from https://github.com/oasis-tcs/odata-openapi

Open a command window go to a directory of you choice to download/clone the toolset from github:

C:\user\project> git clone https://github.com/oasis-tcs/odata-openapi.git
Cloning into 'odata-openapi'...
remote: Enumerating objects: 206, done.
remote: Counting objects: 100% (206/206), done.
remote: Compressing objects: 100% (82/82), done.
remote: Total 4291 (delta 157), reused 164 (delta 120), pack-reused 4085
Receiving objects: 100% (4291/4291), 6.50 MiB | 28.68 MiB/s, done.
Resolving deltas: 100% (3613/3613), done.

As the toolset is cloned to you local repository change into the odata-openapi subfolder an use npm to install the tool locally:

C:\user\project>cd odata-openapi
C:\user\project\odata-openapi> npm install -g
C:\Users\Administrator\AppData\Roaming\npm\odata-openapi3 -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\oda
+ odata-openapi@0.0.0
added 4 packages from 2 contributors in 1.819s
PS C:\user\project\odata-openapi>

With that we have the toolset installed locally and we can continue and create the openApi specification from the oData Service description.

Generate the openApi description the oData services description

Now give the the oData v4 service description document to the tool odata-openapi3 in order to generate the openApi specification from.

C:\user\project\oDataServiceAsOpenApi> odata-openapi3 --host hostname:port .\odata_v4_service_description.xml

Hint: You can get the hostname from Transaction SICF when searching for service „odata4“ and test the service. You see the host..
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
1. Step by Step Archiving Procedure for Material Documents Archiving:
Archiving Object MM_MATBEL is used for the archiving of Material Documents.To perform data archiving in the SAP system, SARA transaction code is used. This is the standard method for executing the Archival.

1.1 Write Job:
1. Go to transaction SARA and enter the Archiving Object MM_MATBEL and hit Enter.

Click on the write button and choose Maintain button to maintain a Variant for the Archival. Enter the Variant name and hit Create button.

Used variants in detail:

MM_MATBEL – Material documents

Archiving should be executed on the following criteria:

– Plant

– Posting Date (from, up to)

– Detail Log “Complete”

– Log Output “Application Log”

Detail log and Log output will be same for all Archiving Objects.

Short texts have to get maintained with the following criteria:

– Object Name

– Company Code

– Fiscal year

– Period

Enter the Posting Date / Plant for which the Archival should happen and click on Variant Attributes button.

Enter the description for the Variant and click on save button.

Click on Back button and the Variant name will appear for Archival as shown below. Click on Start date button, choose immediate button in the screen that follows and click on check and save button

Click on Spool Params. Button, enter LOCL_ZH as Output device and click on Continue button.

Now click on tick button. Archiving Object is ready for Archival, with the Variant being saved.

Click on Execute button as indicated above, for the Archival to happen. Once the Execute button is clicked, the following screen appears.

Click on the Job Overview or check logs in SM37 button, for viewing the Archiving session created.

Choose the corresponding Spool no. in the screen that appears, and click on Display Contents button.

Here in above screen shot you can see ” Material Document archived and deleted”. In AOBJ we have done the customization that once the write job is finished it will automatically triggered deletion job. If, AOBJ was not configured for automatic triggering of deletion job then we need to follow the deletion step separately. I am describing the deletion step below.

1.2 Deletion Job:

1.2. Go back to the initial screen of SARA transaction and click on the Delete button, for the deletion of the Archived records.

Click on Archive selection button, choose the Archiving session number in the screen that appears, by checking the checkbox against it and click on Continue button.

Click on Start date button, choose immediate button from the screen that appears and click on check and save button (as done in Write Step).

Click on Spool params button, enter LOCL as Output device and click on Continue button.

Now the Archiving Object is ready for Deletion.

Click on Execute button as indicated above, for the Deletion to happen. Once the Execute button is clicked, the following screen appears.

Click on the Job Overview button or check logs in SM37.

Deletion Job finished successfully.

1.3 Storage Job

1.3. Go back to the initial screen of SARA transaction and click on the Storage system button, to store the files on storage.

Click on store files button.

Click on Archive Selection, choose the Archiving session number in the screen that appears, by checking the checkbox against it and click on Continue button.

From SM37 or Job overview we can check the job logs for store job.
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Case: Weighbridge scale sends the weight data to COMM Port.

Earlier we had to write a .NET program using SAP.NET Connector (Using VB.NET) and then from SAP side we had to create RFC so that the COMM port data gets utilized in SAP.

Now there is no need to use the third party software (VB.NET, C# etc) Using the following steps we can directly fetch the weight data at the COMM Port to SAP:

Step 1: Create a Function Module (code given below)

Step 2: Register the Windows activeX control MSCOM32.OCX on the client PC where the Weighbridge’s serial port is connected.

Step 3: Implement this control in Transaction SOLE in SAP (Create an entry MSCOMMLIB.MSCOMM.1 and enter the CLSID.        {648A5600-2C6E-101B-82B6-000000000014}

Step 4: Active this MSCOMM32.OCX with Licence Key on the client PC where the Weighbridge’s serial port is connected.

Open RUN execute : regedit

Go to u201CHKEY_CLASSES_ROOT\Licenses\u201D

Create new key (Folder) name with ‘4250E830-6AC2-11cf-8ADB-00AA00C00905’

Give the default VALUE: kjljvjjjoquqmjjjvpqqkqmqykypoqjquoun

Restart the system and run the FM. if the data is coming on the serial port then you will get the result.

FM Code:
FUNCTION z_serial_comport.
""""Local Interface:
  TYPE-POOLS: sabc.
  INCLUDE ole2incl.
  PERFORM init.
  PERFORM open_port USING commport settings.
  IF mode = 0.
    PERFORM read_port
      CHANGING input.
  IF mode = 1.
    PERFORM write_port
      USING output
      CHANGING input.
  PERFORM final.
DATA: o_obj TYPE ole2_object.
FORM init.
    wa_repid LIKE sy-repid.
  wa_repid = sy-repid.
      program          = wa_repid
      activity         = sabc_act_call
      application      = 'MSCOMMLIB.MSCOMM.1'
      no_authority     = 1
      activity_unknown = 2
      OTHERS           = 3.
  IF sy-subrc <> 0.
    MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
            WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  IF sy-subrc <> 0.
    RAISE no_create_object.
ENDFORM.                    " Init
FORM open_port USING commport settings.
  SET PROPERTY OF o_obj 'CommPort' = commport.
  SET PROPERTY OF o_obj 'Settings' = settings.
  SET PROPERTY OF o_obj 'InputLen' = 0.
  SET PROPERTY OF o_obj 'PortOpen' = 1.
ENDFORM.                   "open_port
FORM read_port
  CHANGING input.
    wa_buffer TYPE i.
  DO 10 TIMES.
    GET PROPERTY OF o_obj 'InBufferCount' = wa_buffer.
    IF wa_buffer > 0.
      GET PROPERTY OF o_obj 'Input' = input.
ENDFORM.                    " read_port
FORM write_port
      USING output
      CHANGING input.
    wa_buffer TYPE i.
  SET PROPERTY OF o_obj 'Output' = output.
  DO 10 TIMES.
    GET PROPERTY OF o_obj 'InBufferCount' = wa_buffer.
    IF wa_buffer > 0.
      GET PROPERTY OF o_obj 'Input' = input.
ENDFORM.                    "write_port
FORM final.
  SET PROPERTY OF o_obj 'PortOpen' = 0.
  FREE OBJECT o_obj.
ENDFORM.                    " final
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Open Data Protocol (OData) is an open protocol which allows the creation and consumption of quarriable and inter-operable RESTful APIs in a simple and standard way.

This blog post covers the step by step process for beginners on how to create the OData service for GET operation in the SAP Cloud for Customer System.

The retrieval of the data for GET operation can be achieved in two ways,

1. By Selecting the Standard Business Object
This method of retrieval of data from Standard BO would be quite straight forward. Steps need to be followed

◈ Create OData Service

We need to provide following information

◈ Service Type
◈ Proxy Name
◈ Service Name
◈ Namespace
◈ Package Name (Optional)
◈ Transport Request (Optional)
◈ Exit Class name (explained in later section) (optional)

◈ Select BO

Once service is created Edit the service, and select the BO for which you want to implement the GET service.

◈ Create Entity Types

Once BO is selected Choose the field of Standard BO which should be reflected into the GET service fields these fields are nothing but the Properties of OData service.

◈ Save and Activate the service

After activation of the service, Service URL will be generated on the top. Which will provide metadata information. Which includes entity types, properties and collection name.

◈ By using service URL concatenated with collection name we get the data.

2. By Implementing Custom Class

This type of implementation of OData Service has more to deal with the creation of custom class called Exit class and writing the data retrieval logic in the methods. We use this type of implementation when data is need to be retrieved from the table or non-standard BO or has got some complex logic to fetch the data and which can be accomplished by coding.

Following are the steps involved

◈ Create OData service

Create the OData service, we should provide the exit class name which we will create in the back end.

In my case it is ‘ZODATA_EXIT_CLASS’.

◈ Create Exit class

Create the Exit class which we have mentioned in the OData. We should mention the Super Class ‘CL_AP_ODATA_EXIT’ which will import the interfaces which intern will provide the methods where we accomplish following task by Overriding (re-implementing) the inherited methods

◈ For creating the Entity Type, We should Override the method:


◈ For creating Entity Type Properties, We should Override the method:


◈ Implement the logic for data retrieval

Write the logic for data retrieval in the method IF_AP_ODATA_EXIT~GET_DATA

And assign the data to changing parameter ct_data

Now here we are done with the implementation of the OData service, by browsing the service URL we get details about the service including entity types.

We can  also combine both the methods of creating OData service when we need to design a complex OData to retrieve data from BO and/or from other data source.
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Note: this post is not about the differences between XSLT and ST, it’s just a little example for quickly understanding the general structure of a Simple Transformation.

<?xml version="1.0" encoding="UTF-8"?>
<Name>K. Richards</Name>
<ITM QTY="23" EAN="123123123123" CAT="BL"/>
<ITM QTY="100" EAN="123123123123" CAT=""/>
<ITM QTY="240" EAN="123123123123" CAT=""/>
<ITM QTY="989" EAN="123123123123" CAT=""/>
<ITM QTY="1000" EAN="123123123123" CAT=""/>
<ITM QTY="5" EAN="123123123123" CAT="BL"/>
<ITM QTY="50" SOH="4000299949" EAN="123123123123" SOI="000010" CAT=""/>
<ITM QTY="140" EAN="123123123123" CAT=""/>
<ITM QTY="420" EAN="123123123123" CAT="QI"/>
<ITM QTY="30" EAN="123123123123" CAT=""/>
<ITM QTY="20" EAN="123123123123" CAT="QI"/>
<ITM QTY="475" SOH="4000299949" EAN="123123123123" SOI="000040" CAT=""/>
<ITM QTY="300" EAN="123123123123" CAT=""/>
<ITM QTY="994" EAN="123123123123" CAT=""/>
<ITM QTY="24" EAN="123123123123" CAT="BL"/>
<ITM QTY="3" EAN="123123123123" CAT="BL"/>
<ITM QTY="441" EAN="123123123123" CAT=""/>
<ITM QTY="240" EAN="123123123123" CAT="QI"/>
<ITM QTY="5" EAN="123123123123" CAT=""/>
<ITM QTY="102" EAN="123123123123" CAT="BL"/>
<ITM QTY="2" EAN="123123123123" CAT=""/>
<ITM QTY="360" EAN="123123123123" CAT="QI"/>
<ITM QTY="403" EAN="123123123123" CAT=""/>
<ITM QTY="425" SOH="123123123123" EAN="6941023243415" SOI="000030" CAT=""/>
<ITM QTY="100" EAN="123123123123" CAT=""/>
<ITM QTY="220" EAN="123123123123" CAT=""/>
<ITM QTY="1000" EAN="123123123123" CAT=""/>
<ITM QTY="25" SOH="4000299949" EAN="123123123123" SOI="000020" CAT="BL"/>
<ITM QTY="425" SOH="4000299949" EAN="123123123123" SOI="000020" CAT=""/>
<ITM QTY="340" EAN="123123123123" CAT=""/>
<ITM QTY="1" EAN="123123123123" CAT="BL"/>

The ST
Code of the ST transformation (transaction STRANS – or directly inside Eclipse ADT):

<?sap.transform simple?>
<tt:transform xmlns:tt="http://www.sap.com/transformation-templates">

  <tt:root name="FILE"/>

    <File tt:ref=".FILE"> <!--FILE Needs to match name of RESULT variable in CALL TRANSFORMATION call-->
      <Head tt:ref="HEAD">
        <Name tt:value-ref="NAME"/>
        <tt:loop ref="ITEMS">
              <tt:cond frq="?"><tt:attribute name="QTY" value-ref="QTY"/></tt:cond>
              <tt:cond frq="?"><tt:attribute name="SOH" value-ref="SOH"/></tt:cond>
              <tt:cond frq="?"><tt:attribute name="EAN" value-ref="EAN"/></tt:cond>
              <tt:cond frq="?"><tt:attribute name="SOI" value-ref="SOI"/></tt:cond>
              <tt:cond frq="?"><tt:attribute name="CAT" value-ref="CAT"/></tt:cond>


(almost the same as in the other post, except that I preferred to output the result into a structured variable instead of an internal table, to correspond better to the input XML)


TYPES: BEGIN OF lty_items,
         qty TYPE string,
         soh TYPE string,
         ean TYPE string,
         soi TYPE string,
         cat TYPE string,
       END OF lty_items.

TYPES: BEGIN OF lty_head,
         name TYPE string,
       END OF lty_head.

TYPES: BEGIN OF lty_out,
         head  TYPE lty_head,
       END OF lty_out.

DATA: lv_xml TYPE string,
      lt_xml TYPE STANDARD TABLE OF string,
      ls_out TYPE lty_out.

PARAMETERS p_file TYPE string LOWER CASE DEFAULT 'C:\link\to\test.xml'.

"Load xml file
    filename = p_file
    filetype = 'ASC'
    data_tab = lt_xml
    OTHERS   = 19 ).

lv_xml = concat_lines_of( lt_xml ).

  SOURCE XML lv_xml
  RESULT file = ls_out.
cl_demo_output=>new( )->write_data( ls_out-head
                     )->write_data( ls_out-items )->display( ).

The result
(running the ABAP program -> contents of variable LS_OUT)

Now for some pointers
Here are some important tips

1. ST has a syntax which usually allows the same transformation being called in both directions, from XML to ABAP, but also from ABAP to XML. It’s a great feature, but it also complicates the ST syntax, even if you want to use the transformation only in one direction, especially for optional elements (for instance, tt:group and tt:cond frq=”?” altogether allow the attributes to be optional and in any position)

2. ST is not able to convert from XML to XML (XSLT can do it), but “thanks to that” ST doesn’t require to declare the asXML elements (<asx:abap…; they are required only when you write an XSLT transformation for transforming from or to ABAP).

3. Whatever the direction is, the transformation is always to be written with the XML elements as being elements in the transformation (<element …>), and ABAP component names as being in value-ref and ref attributes (you can see what I mean by comparing the ST and XSLT transformations, they are different although the XSLT writes in the same direction i.e. XML to ABAP).

4. Always respect the case of XML words, it’s very important in all XML languages!
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
Comparative Analysis is a common requirement, for example, Y2Y. The challenge is to model CDS View in such a way that make data selection flexible and easy for both reporting period and reference period.

Below are two examples of comparative analysis in Query Browser. One for QTD and another one for YTD. QTD 2019 and FTD 2019 are compared respectively with QTD 2018 and FTD 2018. What user needs to enter is just rolling period name (Date Function) and CDS view calculates both Seat Occ Rate for selected period and Seats Occ Rate Ref for reference period (selected period offset by either 365 or 366 days).

The same examples of QTD and YTD comparative analysis in Smart Business:

CDS views are modeled in such a way that:

◈ There are two sets of measures one for reporting period and another one for reference period;
◈ Reporting period is selected by Date Function (rolling period) which is converted to date range selection by C_SglGregorianCalDateFunction
◈ Date Function parameter value help is assigned for ease of use;
◈ ZX_CalendarDate View Extension and ZI_CalendarDate Basic View are used to offset reporting period date range by either 365 or 366 days to get reference period date range

Following CDS views need to be created for the demo:

◈ ZI_DateFunction date function value help view
◈ ZX_CalendarDate calendar date extension view
◈ ZI_CalendarDate calendar date basic view
◈ ZSAPBC_Carr carrier basic view
◈ ZSAPBC_Region region basic view
◈ ZSAPBC_CarrText carrier text view
◈ ZSAPBC_CarrDimension carrier dimension view
◈ ZSAPBC_RegionHierarchy region hierarchy view
◈ ZSAPBC_RegionDimension region dimension view
◈ ZSAPBC_FlightFact flight fact table view
◈ ZSAPBC_FlightCube flight cube
◈ ZSAPBC_FlightQuery flight query

ZI_DateFunction Date Function Value Help View

@AbapCatalog.sqlViewName: 'ZIDATEFUNC'
@EndUserText.label: 'Date Function'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@ClientHandling.algorithm: #SESSION_VARIABLE
@VDM.viewType: #BASIC
define view ZI_DateFunction as select from C_GregorianCalDateFunction( P_Language: $session.system_language )

Note: I_CalendarDate view is extended with CalendarYearMinus1 field. This field will be used late on for leap year offset

ZX_CalendarDate Calendar Date View Extension

@AbapCatalog.sqlViewAppendName: 'ZXCALDATE'
@EndUserText.label: 'Date'
extend view I_CalendarDate with ZX_CalendarDate
  association [0..1] to I_CalendarYear as _CalendarYearMinus1 on $projection.CalendarYearMinus1 = _CalendarYearMinus1.CalendarYear 
  @ObjectModel.foreignKey.association: '_CalendarYearMinus1'
  case when calendaryear = '0000'  
       then calendaryear
       else cast(cast(cast(calendaryear as abap.int2) - 1 as abap.char( 12 ) ) as abap.numc( 4 ) ) end as CalendarYearMinus1,

ZI_CalendarDate Calendar Date Basic View

@AbapCatalog.sqlViewName: 'ZICALENDARDATE'
@EndUserText.label: 'Date'
define view ZI_CalendarDate as select from I_CalendarDate 
  key CalendarDate,
  case when ( ( _CalendarYear.IsLeapYear = 'X' and CalendarMonth = '02' and CalendarDay = '29' ) or 
              ( _CalendarYear.IsLeapYear = 'X' and CalendarMonth  > '02' ) )
       then dats_add_days( CalendarDate, -366, 'FAIL' ) 
       when ( ( _CalendarYearMinus1.IsLeapYear = 'X' and CalendarMonth = '02' and CalendarDay < '28' ) or
              ( _CalendarYearMinus1.IsLeapYear = 'X' and CalendarMonth = '01' ) )
       then dats_add_days( CalendarDate, -366, 'FAIL' )
       else dats_add_days( CalendarDate, -365, 'FAIL' ) end as CalendarDateMinus1Year,

Note: whenever offset cross February 29 of leap year then it is required to subtract 366 days instead of 365

ZSAPBC_Carr Carrier Basic View

@AbapCatalog.sqlViewName: 'ZCARR'
@VDM.viewType: #BASIC 
@EndUserText.label: 'Airline'
define view ZSAPBC_Carr as select from scarr
  carrid as CarrID,   
  url as Url,
  currcode as CurrCode,
  when carrid = 'LH' or carrid = 'AB' then 'Germany'
  when carrid = 'AA' or carrid = 'CO' or carrid = 'DL' or 
       carrid = 'NW' or carrid = 'WA' then 'US'
  when carrid = 'AC' then 'Canada'  
  when carrid = 'AF' then 'France'  
  when carrid = 'AZ' then 'Italy'
  when carrid = 'BA' then 'UK'
  when carrid = 'FJ' then 'Fiji'
  when carrid = 'NG' then 'Austria'
  when carrid = 'JL' then 'Japan'
  when carrid = 'QF' then 'Australia'
  when carrid = 'SA' then 'South Africa'
  when carrid = 'SQ' then 'Singapure'
  when carrid = 'SR' then 'Swirzerland'  
  when carrid = 'UA' then 'US'
  else 'Other'
  end as abap.char( 13 )) as Region    

ZSAPBC_Region Region Basic View

@AbapCatalog.sqlViewName: 'ZREG'
@EndUserText.label: 'Region'
@VDM.viewType: #BASIC
define view ZSAPBC_Region as select distinct from ZSAPBC_Carr {
  key Region,
  when Region = 'Germany' or Region = 'France' or Region = 'Italy' or
       Region = 'UK' or Region = 'Austria' or Region = 'Swirzerland' then 'Europe'
  when Region = 'US' or Region = 'Canada' then 'North America'
  when Region = 'South Africa' then 'Africa' 
  when Region = 'Fiji' or Region = 'Japan' or Region = 'Singapure' then 'Asia'
  else 'Other' 
  end as MainRegion  
where Region <> 'Australia'


select distinct from scarr {

key 'Europe' as Region,
    'World' as MainRegion


select distinct from scarr {

key 'North America' as Region,
    'World' as MainRegion


select distinct from scarr {

key 'Asia' as Region,
    'World' as MainRegion


select distinct from scarr {

key 'Australia' as Region,
    'World' as MainRegion


select distinct from scarr {

key 'Africa' as Region,
    'World' as MainRegion


select distinct from scarr {

key 'World' as Region,
    '' as MainRegion

ZSAPBC_CarrText Carrier Text View

@AbapCatalog.sqlViewName: 'ZCARRTEXT'
@Analytics: {dataCategory:  #TEXT, dataExtraction.enabled: true}
@AccessControl.authorizationCheck: #NOT_ALLOWED
@EndUserText.label: 'Carrier'
define view ZSAPBC_CarrText as select from scarr {
  key carrid as CarrId,
  @Semantics.text: true
  carrname as CarrierName    

ZSAPBC_CarrDimension Carrier Dimension View

@AbapCatalog.sqlViewName: 'ZCARRDIM'
@Analytics: {dataCategory: #DIMENSION, dataExtraction.enabled: true} 
@ObjectModel.representativeKey: 'Carrid'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Carrier'
define view ZSAPBC_CarrDimension as select from ZSAPBC_Carr
 association [0..1] to ZSAPBC_CarrText as _Text on $projection.CarrID = _Text.CarrId
  @ObjectModel.text.association: '_Text' 
  key CarrID,
  @EndUserText.label: 'Region'

ZSAPBC_RegionHierarchy Region Hierarchy View

@AbapCatalog.sqlViewName: 'ZREGIONHIER'
@Analytics: { dataCategory: #HIERARCHY, dataExtraction.enabled: true }
@ObjectModel.representativeKey: 'Region'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Region'
@Hierarchy.parentChild.name: 'REGION_GEO'
@Hierarchy.parentChild.label: 'Region Geography'
{ recurse:          {   parent: 'ParentNode',   child:  'HierarchyNode'   } }
define view ZSAPBC_RegionHierarchy as select distinct from ZSAPBC_Region 
 association[0..1] to ZSAPBC_RegionDimension as _Dimension on $projection.HierarchyNode = _Dimension.Region
  @ObjectModel.foreignKey.association: '_Dimension'
  key Region as HierarchyNode,
  MainRegion as ParentNode,

ZSAPBC_RegionDimension Region Dimension View

@AbapCatalog.sqlViewName: 'ZREGIONDIM'
@Analytics: { dataCategory: #DIMENSION, dataExtraction.enabled: true }
@ObjectModel.representativeKey: 'Region'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Region'
define view ZSAPBC_RegionDimension as select from ZSAPBC_Region
association[0..*] to ZSAPBC_RegionHierarchy as _Hierarchy on $projection.Region = _Hierarchy.HierarchyNode
  @ObjectModel.Hierarchy.association: '_Hierarchy' 
  key Region,
  @EndUserText.label: 'Main Region'

ZSAPBC_FlightFact Flight Fact Table View

@AbapCatalog.sqlViewName: 'ZSFLIGHTFACT'
@AbapCatalog.compiler.compareFilter: true
@Analytics.dataCategory: #FACT
@EndUserText.label: 'Flight'
define view ZSAPBC_FlightFact with parameters
    @Consumption.defaultValue: 'CAD' 
    P_DisplayCurrency : s_currcode  
    as select from sflight inner join ZSAPBC_Carr as scarr 
                                   on sflight.carrid = scarr.CarrID {
  key sflight.carrid as CarrId,
  key sflight.connid as ConnId,
  key sflight.fldate as FlightDate,
  scarr.Region as Region,  
  $parameters.P_DisplayCurrency as Currency,
    amount             => sflight.paymentsum,
    source_currency    => sflight.currency,
    target_currency    => $parameters.P_DisplayCurrency,
    exchange_rate_date => sflight.fldate,
    exchange_rate_type => 'M',
    error_handling     => 'SET_TO_NULL'              // otherwise data inconsistencies cause a dump     
  ) as Payment,
  cast('EA' as abap.unit(3) ) as Unit,
  seatsmax + seatsmax_b + seatsmax_f as SeatsMax,
  seatsocc + seatsocc_b + seatsocc_f as SeatsOcc    

ZSAPBC_FlightCube Flight Cube View

@AbapCatalog.sqlViewName: 'ZSFLIGHTCUBE'
@AbapCatalog.compiler.compareFilter: true
@Analytics: { dataCategory: #CUBE, dataExtraction.enabled: true }
@EndUserText.label: 'Flight Cube'

define view ZSAPBC_FlightCube with parameters
  P_DisplayCurrency : s_currcode, 
  P_StartDate: dats,
  P_EndDate: dats       
  as select from ZSAPBC_FlightFact( P_DisplayCurrency:  $parameters.P_DisplayCurrency ) as flight inner join I_CalendarDate as Calendar on flight.FlightDate = Calendar.CalendarDate 
    association [1..1] to ZSAPBC_CarrDimension as _Carr on  $projection.CarrId  = _Carr.CarrID
    association [1..1] to ZSAPBC_RegionDimension as _Region on  $projection.Region  = _Region.Region
  key 'CURR' as PeriodType,
  @ObjectModel.foreignKey.association: '_Carr'  
  key flight.CarrId,
  key flight.ConnId,
  @EndUserText.label: 'Date'  
  key Calendar.CalendarDate as FlightDate,
  @EndUserText.label: 'Region'
  @ObjectModel.foreignKey.association: '_Region'
  @Semantics.calendar.yearWeek: true
  @EndUserText.label: 'Week'  
  Calendar.YearWeek as FlightWeek,  
  @Semantics.calendar.yearMonth: true
  @EndUserText.label: 'Month'  
  Calendar.YearMonth as FlightMonth,
  @Semantics.calendar.year: true
  @EndUserText.label: 'Year'
  Calendar.CalendarYear as  FlightYear,
  @EndUserText.label: 'Currency'  
  @Semantics.unitOfMeasure: true  
  @EndUserText.label: 'UOM'  
  @Semantics.unitOfMeasure: true
  @EndUserText.label: '%'  
  cast( 'Z%' as abap.unit( 3 ) ) as PercentUnitOfMeasure,    
  @Semantics.amount.currencyCode: 'currency'
  @EndUserText.label: 'Payment'
  @DefaultAggregation: #SUM
  @Semantics.quantity.unitOfMeasure: 'unit'
  @DefaultAggregation: #SUM  
  @EndUserText.label: 'Seats Max'
  @Semantics.quantity.unitOfMeasure: 'unit' 
  @DefaultAggregation: #SUM  
  @EndUserText.label: 'Seats Occ'
  @Semantics.amount.currencyCode: 'currency'
  @EndUserText.label: 'Payment Ref'
  @DefaultAggregation: #SUM
  cast(0 as s_sum) as payment_ref,
  @Semantics.quantity.unitOfMeasure: 'unit'
  @DefaultAggregation: #SUM  
  @EndUserText.label: 'Seats Max Ref'
  cast(0 as s_seatsmax) as SeatsMaxRef,
  @Semantics.quantity.unitOfMeasure: 'unit'
  @DefaultAggregation: #SUM  
  @EndUserText.label: 'Seats Occ Ref' 
  cast(0 as s_seatsocc) as SeatsOccRef,
  @Semantics.quantity.unitOfMeasure: 'PercentUnitOfMeasure'
  @DefaultAggregation: #MAX
  @EndUserText.label: '100%'
  100 as HundredPercent, 
 where flight.FlightDate between $parameters.P_StartDate and $parameters.P_EndDate 

 union all

  select from ZSAPBC_FlightFact( P_DisplayCurrency:  $parameters.P_DisplayCurrency ) as flight
    inner join ZI_CalendarDate as Calendar on flight.FlightDate = Calendar.CalendarDateMinus1Year 
    association [1..1] to ZSAPBC_CarrDimension as _Carr on  $projection.CarrId  = _Carr.CarrID
    association [1..1] to ZSAPBC_RegionDimension as _Region on  $projection.Region  = _Region.Region
  key 'PREV' as PeriodType,
  @ObjectModel.foreignKey.association: '_Carr'  
  key flight.CarrId,
  key flight.ConnId,
  @EndUserText.label: 'Date'  
  key Calendar.CalendarDate as FlightDate,
  @EndUserText.label: 'Region'
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
You use CDS annotations to enrich your data model with metadata. In some cases, this can reduce readability and make it hard to maintain your data definitions. To improve this situation, metadata extensions have been introduced.

Metadata Extensions
With metadata extensions you can modularize the annotations within your data definitions and add customer-specific metadata to SAP entities modifying the data definition. It’s a transportable ABAP development object with its own Dictionary folder in the Project Explorer.

Using Refactoring Features and Templates
To improve the readability of your data definition you can extract annotations from the data definition into a separate metadata extension. Just select the relevant annotations in the CDS view with the mouse and choose “Source Code” > “Extract Metadata Extension” from the context menu.

The extraction wizard for creating a new metadata extension is opened.

Here the same logic is used as for the rest of the development objects.

Templates provide you a predefined set on code.

I am sure, you know what to do. After finishing, the selected annotations are removed from the CDS view and added to the new metadata extension.

But you don’t have to extract all annotations to make your code clearer. You can also show and hide all annotations for a quick check. To do this, right click in the ruler bar beside the editor.

From this menu, you can choose the relevant entry.

Finally, I want to highlight code folding. It allows you to collapse coherent parts of source code such as SQL clauses, CDS annotations, and comments. You can trigger it from the same menu as hiding comments/annotations.

If you choose “Collapse All”, the annotations are hidden:

The fact that the annotations are collapsed is highlighted by the sign at the beginning and the double dot indicator at the end of the row.
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
This is about featuring the capability of Value help on a selection field within a value help dialog in Fiori Elements based List Report.

Business Scenario
Let’s consider Business scenario where Fiori Elements based List Report displays Purchase Order with Vendor as one of the selection field.

“Vendor can be further filtered based on selected countries.”

Technical Explanation

1. Develop main consumption CDS For List Report and add relevant UI annotations for Line Items, Selection Fields.

2. Add Value help specific annotation for the selection field, provide reference of Value help CDS  using annotation “@Consumption.valueHelpDefinition:

3. To bring “Value help within a value help dialog”, Add further value help CDS reference inside      the value help CDS used in step 3.

4. Data Model can be understood as below:

5. CDS can be exposed as ODATA or SEGW reference data source and generate the Fiori elements-based application.

Detailed Explanation

Let’s create the supplier Value help CDS(ZI_SUPPLIER_VH), Inside this give the reference of Standard CDS I Country for the countries list.

Now, Lets create the main consumption CDS for the List Report (ZC_PurchaseOrder). Add UI annotation for Line Items and selection fields. Give the reference of ZI_SUPPLIER_VH.

Code snippet FOR the Consumption CDS

Value Help CDS

Demo (Output)

Fiori Elements with Vendor as selection parameter

Select Country for further value help

Selected country become available for searching the supplier further

And Finally, selected Supplier available in the List Report selections parameter

Read for later

Articles marked as Favorite are saved for later viewing.
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Separate tags by commas
To access this feature, please upgrade your account.
Start your free month
Free Preview