Use of new ABAP 7.4 syntax

ABAP 7.4 has many nice syntax improvements. These enhancements increase the efficiency of the code written, making it easier for developers to use and to read the code.

Key features:

  • Inline Declarations
  • Table Expressions
  • Constructor Operator NEW
  • Value Operator VALUE
  • FOR operator
  • Reduction operator REDUCE
  • Conditional operators COND and SWITCH
  • CORRESPONDING operator
  • Strings
  • Filter

Inline Declaration(DATA)

Inline declarations are a new concept of declaring variables and field symbols. This enables programmer to declare the variable at the time of they used. With this new format the compiler knows that the data store the value in the form of string variable. No need to explicitly declare the string variable. So, the line of code got reduced.

Before 7.4

DATA zlv_text TYPE string. 
zlv_text = 'ABC’.

With 7.4

DATA(zlv_text) = 'ABC'.

Inline Declaration(Loop at into work area)

Every time we need to loop over an internal table we need work area . Many times this work area is not in use when loop processing is completed. Hence inline declaration make it easy to declare it and use it when it is required.

Before 7.40

Types: Begin of zlts_itab, 
Matnr type matnr,
            Sernr type gernr,
            end of zlts_itab,

DATA : zlt_itab type table of zlts_itab,  
             zls_wa type zlts_itab.
LOOP AT zlt_itab INTO zls_wa.
......
ENDLOOP. 

With 7.40

LOOP At zlt_ itab INTO DATA(zls_wa). 
…………
ENDLOOP.

Inline Declaration(Field Symbol)

Like work area we can use inline declaration for Field symbol. Here we can use angular bracket and FIELD_SYMBOL keyword as before. Read a line of internal table and assign it to a field symbol.

Before 7.40

FIELD_SYMBOLS <zls_test> type zlts_itab.
LOOP AT zlt_itab assigning <zls_test>
.....
ENDLOOP.

Read table zlt_ itab assigning <zls_test>.

With 7.40

LOOP At zlt_ itab assigning FIELD_SYMBOL (<zls_test>).
…………

ENDLOOP.
Read table zlt_itab assigning FIELD-SYMBOL(<zls_test>).

Inline Declaration(Select into table)

We need to put @symbol in front of the variables using in the select statement while using new feature. That helps compiler to understand that we are not referring the field of the database but the variable in the program.

No need to declare the internal table separately.

Please note when we use new feature we need to put comma between field while retrieving data from DB table, and also put into statement at the end.

Before 7.40

DATA : zlv_fld1 type matnr  value ‘number’,
zlt_itab TYPE TABLE OF mara.

SELECT * FROM mara
INTO TABLE zlt_itab
WHERE matnr = zlv_fld1.

With 7.40

SELECT * FROM mara 
INTO TABLE @DATA(zlt_itab)
WHERE matnr = @zlv_fld1.

Inline Declaration(Call Method)

Inline ABAP DATA declarations are a new concept introduced in release 7.4 which allows you to declare your internal table variables or work areas within the code that uses them.

No need to declare the internal table separately.

Before 7.40

DATA zlv_a1 TYPE ...
DATA zlv_a2 TYPE ...

zlo_oref->meth( IMPORTING p1 = zlv_a1
                             IMPORTING p2 = zlv_a2 ).

With 7.40

zlo_oref->meth( IMPORTING p1 =DATA(zlv_a1)
IMPORTING p2 = DATA(zlv_a2) ).

Table Expression(Read Table Index)

Previously we saw we can read the internal table to work area without declaring work area before the read statement.

In new feature we can see we might never have to use Read statement at all.

If we want to read 3rd line from Internal table we need to pass 3 into the variable idx = 3.

Before 7.40

READ TABLE zlt_itab INDEX zlv_indx  INTO zls_wa.

With 7.40

zls_wa =zlt_itab[ zlv_idx ].

Table Expression(Read Table using key and with key)

In this example we are reading internal table with key and if the passing parameter values and it satisfied then work area will be populated. If not then sy-subrc will not equal to 0.

But in new statement we can simply write work area equal to…it simple replace the read statement with direct assignment of table details to work area.

Before 7.40

1. READ TABLE zlt_itab INDEX zlv_idx
USING KEY key INTO zls_wa.

2. READ TABLE zlt_itab
               WITH KEY col1 =
                        col2 = …  INTO zls_wa.

3. READ TABLE zlt_itab WITH TABLE KEY key
COMPONENTS col1 = …
                              col2 = …INTO zls_wa.

With 7.40

zls_wa = zlt_itab[ KEY key INDEX zlv_idx ].

zls_wa = zlt_itab[ col1 = … col2 = … ].

zls_wa = zlt_itab[ KEY key col1 = …col2 = … ].

Table Expression (Does record exist?)

Whenever we have data in internal table it is very common practice to check the data which we require from table that is already exist or not?

We use here TRANSPORTING NO FIELDS statement  as we are not interested about the data but it is exist or not in the table.

In new statement we use line_exist statement to do this. We use the statement then internal table and the parameters if it is successful then compiler go inside.

Before 7.40

READ TABLE zlt_itab ... TRANSPORTING NO FIELDS.
IF sy-subrc = 0.   
...
ENDIF.

With 7.40

IF line_exists( zlt_itab[ ... ] ).      
...
ENDIF.

Table Expression(Get table index)

This example is used to read the index table index. We use line_index in the new statement.

Before 7.40

DATA zv_idx type sy-tabix.
READ TABLE ...
TRANSPORTING NO FIELDS.

zv_idx = sy-tabix.

With 7.40

DATA(zv_idx) = line_index(zlt _itab[ ... ] ).

String processing

This is frequently used in ABAP code. The data from the DB needs to be formatted before display and vice versa.

Concatenation of string: the changes are as followed:

  • String template: Create character string out of literals texts, expressions and control character, The goal is to display data in more human readable format.
  • We use pipe symbol and && operator to concatenate string.
  • To replace the write to statement.
  • A string template is defined by using the |(pipe) symbol at the beginning and end of a template.
  • If we add some value with string template like {expression} and control character.
  • Embedded expression are defined within the string template with curly brackets. Note a space between bracket and expression is obligatory.
  • An expression can be variable, functional method, predefined function or calculation expression.

Before 7.4

Data zlv_qmnum type string.
Data zlv_string type string.

zlv_qmnum = zls_qmel-qmnum.

Concatenate ‘Your notification no:’
                           zlv_qmnum
                           into zlv_string
                           separated by space.

Write :/ zlv_string.

With 7.4

Data zlv_qmnum type string.
Data zlv_string type string.

zlv_qmnum = zls_qmel-qmnum.
zlv_string = |’Your Notification No:’|&& zlv_qmnum.
zlv_string = ’Your Notification No:’.

Same as (as soon as we add pipe symbol it became string template).

zlv_string = |’Your Notification No:’.|.
zlv_string = |{ a numeric variable }|.
zlv_string = |’The return code is: ({ sy-subrc }|.
zlv_string = |’The length of text : {( text-001 )} is { strlen(text-001) }.

Chaining Operator: The chaining operator && can be used to create one character string out of multiple other strings  and string template.

In this example a number text , a space, an existing character string and a new string template are concatenated into new character string. The code convert the amount field in a display format as per user settings.

Character_string = ‘Text literal(002)’&& ‘ ’&& character_string && |{ amount_field NUMBER = USER }|.

Conversion operator

Conversion operator is used to convert value into specified type. It is suitable to avoid the declaration of helper variable.

For example :let us assume that a method expects string but we have data in character. We would need to move the  value to the string variable and then pass the helper variable to the method call. With CONV variable helper variable not required. The value can be converted directly during method call.

Before 7.40

Data zlv_cust_name type c length 20.
Data:zlv_helper type string.

zlv_helper = zlv_cust_name.

Cl_func->process_func(ziv_input = zlv_helper).

With 7.40

Data zlv_cust_name type c length 20.
Cl_func->process_func(ziv_input =CONV string( zlv_cust_name ).

Cl_func->process_func(ziv_input =CONV #(zlv_cust_name ).

Casting operator

The casting operator CAST is a constructor operator that performs a down cast or up cast for the object and create a reference variable as a result.

Syntax of casting operator is :

Cast #/type( [let_exp] dobj)

Type can be class or interface. The # character is a symbol for the operand type.

With 7.40

CLASS zcl1 DEFINITION.
ENDCLASS.
CLASS zcl2 DEFINITION INHERITING FROM    zcl1.
ENDCLASS.

DATA: zlo_oref1 TYPE REF TO zcl1,
      zlo_oref2 TYPE REF TO zcl2.

IF zlo_oref1 IS INSTANCE OF zcl2.
  zlo_oref2 ?= zlo_oref1.
  zlo_oref2 =  CAST #( zlo_oref1 ).
ENDIF.

Value operator

The VALUE operator in SAP ABAP 7.4 is used to create and initialize data objects of a specified type. It is particularly useful for constructing values for structured types, table types, and controlling the type of results in table expressions.

Initial Value for All Types:

The VALUE operator can be used to create initial values for any non-generic data types

DATA zlv_initial TYPE i
zlv_initial = VALUE i( )

Structures

For structures, the VALUE operator allows you to specify values for individual fields

TYPES: BEGIN OF zlts_struct,
field1 TYPE string,
field2 TYPE string,
END OF zlts_struct

DATA zls_struct TYPE zlts_struct

zls_struct = VALUE zlts_struct( field1 = 'Value1' field2 = 'Value2' )

Internal Tables

The VALUE operator can also be used to construct internal tables

DATA zlt_table TYPE TABLE OF string
zlt_table = VALUE #( ( 'Row1' ) ( 'Row2' ) ( 'Row3' ) )

For operator

For operator is used to loop at an internal table. For each loop the row is read and assigned to a work area or field symbol. This is similar to the same FOR loop in C language.

Transfer data from one internal table to another internal table:

New Syntax:

Data: zlt_r_equipment TYPE RANGE OF equnr,
zlt_r_equipment = VALUE #(  FOR zls_equipment IN zlt_r_equipment
                    ( sign   = zls_equipment-sign
                      option = zls_equipment-option
                      low    = |{ zls_equipment-low   ALPHA = IN }|
                      high   = |{ zls_equipment-high ALPHA = IN }| ) ).
).

Reduction operator

Reduce operator creates a results of specified data type after going through iteration. In Classical ABAP if we had to evaluate the data in an internal table then we had to loop through the internal table, evaluate the condition, and then take appropriate action. This could be done much simpler way using REDUCE.

Data(zlv_lines) = REDUCE i ( init x = 0 for wa_sales in lt_sales where ( salesoffice = ‘IN’) Next x= x+1 ) .

Conditional operator

It is an accepted practice in ABAP to use CASE statement instead of IF statement. CASE statement made the code readable but had an issue that it was not able to evaluate multiple condition.so we use if else statement. In 7.40 we use COND operator for the same.

Data(zlv_text) = COND # ( when zlv_qmtxt = ‘X1’and zlv_artpr = ‘XX’ then ‘Customer’
when zlv_qmtxt = ‘01’and zlv_artpr = ‘XX’ then ‘test1’
when zlv_qmtxt = ‘02’and zlv_artpr = ‘XX’ then ‘test2’).

Write:/zlv_text

Switch operator

Switch operator is a conditional operator like CASE but more powerful and less code. It is used to switch from one value to another based on condition.

Old syntax:

data: zlv_indicator like scal-indicator, zlv_day(10) type c.
    case zlv_indicator.
    when 1.
      zlv_day = 'Monday'.
    when 2.
      zlv_day = 'Tuesday'.
    when 3.
      zlv_day = 'Wednesday'.
    when 4.
      zlv_day = 'Thursday'.
    when 5.
      zlv_day = 'Friday'.
    when 6.
      zlv_day = 'Saturday'.
    when 7.
      zlv_day = 'Sunday'.
else.
     Raise exception type zcx_day_problem.
endcase.

New Syntax:

DATA(zlv_day) = SWITCH char10( zlv_indicator
when 1 THEN 'Monday'
   when 2 THEN 'Tuesday'
   when 3 THEN 'Wednesday'
   when 4 THEN 'Thursday'
   when 5 THEN 'Friday'
   when 6 THEN 'Saturday'
   when 7 THEN 'Sunday'
   ELSE THROW zcx_day_problem( ) ).

Corresponding operator

Corresponding operator allows the copy of data from one internal table to another internal table just like move corresponding but provides more options on which columns are copied.

TYPES : BEGIN OF zlts_demo1,
col1 TYPE c,
          col2 TYPE c,
        END OF zlts_demo1,

      BEGIN OF zlts_demo2,
          col1 TYPE c,
          col3 TYPE c,
          col4 TYPE c,
        END OF zlts_demo2.

Data: zlt_itab1 TYPE STANDARD TABLE OF zlts_demo1,
      zlt_itab2 TYPE STANDARD TABLE OF zlts_demo2.

zlt_itab1 = VALUE #( ( col1 = 'A' col2 = 'B' )
                 ( col1 = 'P' col2 = 'Q' )
                  ( col1 = 'N' col2 = 'P' ) ).

zlt_itab2 = CORRESPONDING #(zlt_itab1 ).

cl_demo_output=>write_data( zlt_itab1 ).
cl_demo_output=>write_data( zlt_itab2 ).

cl_demo_output=>display( ).

Filter

A new FILTER operator is available which can used on ABAP internal tables to filter the data (or) to retrieve subset of data into a new internal table. As of ABAP 7.4 this keyword is available to use in the system.

Old Syntax:

Data: zlt_qmel_all type standard table of qmel,
zlt_qmel_zz type standard table of qmel.

Select * from qmel into table @zlt_qmel_all.

If sy-subrc = 0.
Loop at zlt_qmel_all into data data(zls_qmel) where qmart = ‘00’.
Append zls_qmel to zlt_qmel_zz.
Clear : zls_qmel.
Endloop.
Endif.

New Syntax:

zlt_qmel_zz = FILTER #(zlt_qmel_all using key qmart where qmart = ‘00’)

ABAP Clean Core development

ABAP Clean Core is a development concept. It is not to be confused with ABAP clean code.

The ABAP Clean Core is fully explained in this very extensive SAP document: Extend SAP S/4HANA in the cloud and on premise with ABAP based extensions.

SAP has a positioning of development patterns and tools from the past. This is written in OSS note 3578329 – Frameworks, Technologies and Development Patterns in Context of Clean Core Extensibility.

This blog will focus on the initial phase to get insights into your existing code and to brief you on the main changes.

Prepare the ATC run for Clean Core

First step is to prepare the ATC runs by applying OSS note 3565942 – ATC Checks “Usage of APIs” and “Allowed Enhancement Technologies” (don’t forget this action After implementation, press ‘Import Parameters’ for the ATC check object SYCM_USAGE_OF_APIS (in ADT for Eclipse).).

Then use the Notes Analyzer to apply correction notes from OSS note 3627152 – SAP Note Analyzer Files for ATC Checks Related to Clean Core.

ABAP CVA security checks is part of this variant. If you run on SAP cloud this is part of license, on premise it is not (so you need separate license). If you don’t have the license use ABAP Eclipse ADT to remove the check from ATC variant ABAP_CLOUD_DEVELOPMENT_DEFAULT.

To make sure all new items are loaded, start transaction SCI and choose the option Utilities / Import Check Variants.

ATC runs for Clean Core

Run the ATC for variant ABAP_CLOUD_DEVELOPMENT_DEFAULT.

Remark: please read the document Extend SAP S/4HANA in the cloud and on premise with ABAP based extensions if there a newer version defined!

Now run this one (or older ABAP_CLOUD_READINESS) on this simple test program:

REPORT ztest.

DATA: zlt_mara TYPE TABLE OF mara.
DATA: zls_mara TYPE mara.

SELECT * FROM mara INTO zls_mara.
ENDSELECT.

Result:

Already in this small piece of code 2 showstoppers:

  • You cannot use SE38 programs any more with REPORT statement
  • Direct table reads (in this case MARA) are forbidden in Clean Core

When you run on your existing code base, you will find many issues. On average a single old fashioned written ABAP code easily generates 100 clean core findings or more.

Forbidden to use in Clean Core

What else is not allowed?

Short list:

  • ALV grid output
  • Enjoy screens
  • SAP script
  • Smartforms
  • Webdynpro
  • Non released function modules
  • Batch input
  • Many more

New technology

So what do I need to use?

  • Data selection: CDS views
  • User interaction: FIORI (or FIORI elements) including FIORI key user extensibility
  • Data and processing logic: RAP (restful application programming) framework
  • Use released API’s (see the Cloudification Repository Viewer, link and explanation)
  • SAP extension points

BTP side by side developments

BTP side by side developments are an option. These developments are meant for loosely coupled scenarios. Also check if the BTP platform availability restrictions meet your SLA requirements.

Summary

Starting with a Green Field new SAP implementation, there is a good chance you will succeed in applying the Clean Core principles.

With an existing implementation there are a couple of bottlenecks:

  • Most likely skill set of you current ABAP developers is not up to par to meet the Clean Core development skill set
  • Small changes to existing code you will keep doing old style, since the effort to convert is out of balance with the effort of doing old style
  • The new style of coding is more structured and takes more initial effort (which will be regained at the end in the maintenance phase), but this can be a hard sell. Old fashioned ABAP with selection screen, direct SQL and ALV output is very effective and efficient to write in terms of man days work.
Tip: try the new development style for a completely new development. The chance of success will rise.

Upload data from file into Z table

With the coding below you can use a file to upload data into any Z table. The program below first deletes the full content first, then upload the content from the file. If you want different behavior, you can adjust the program to your own needs.

The program is to be protected with a proper authorization check. And it is for utility support only for non-productive systems. Do not use on productive systems.

Be careful: the program first deletes ALL the content of the current table. Then it inserts the entries from the file.

Program selection screen:

Coding:

*&--------------------------------------------------------------------*
*& Report Z_UPLOAD_TABLE
*&--------------------------------------------------------------------*
*& Description: Upload the data from a file and fill a Z table
*&--------------------------------------------------------------------*

REPORT z_upload_table.

PARAMETERS:
  p_table TYPE dd02l-tabname OBLIGATORY,
  p_file  TYPE ibipparms-path OBLIGATORY.

DATA: lt_file_data  TYPE STANDARD TABLE OF string,
      lt_table_data TYPE REF TO data,
      lt_fieldcat   TYPE lvc_t_fcat,
      lt_component  TYPE abap_component_tab,
      lv_separator  TYPE c LENGTH 1 VALUE ','.
DATA: lv_offset TYPE i VALUE 0,
      lv_until  TYPE i  VALUE 0,
      lv_field  TYPE string.
DATA: lv_filename TYPE string.

FIELD-SYMBOLS: <lt_table_data> TYPE STANDARD TABLE,
               <ls_table_data> TYPE any,
               <lv_field>      TYPE any.

DATA: new_line  TYPE REF TO data.

AT SELECTION-SCREEN.
  DATA: l_got_state TYPE  ddgotstate.
* Validate table name
  CALL FUNCTION 'DDIF_TABL_GET'
    EXPORTING
      name     = p_table
    IMPORTING
      gotstate = l_got_state
    EXCEPTIONS
      OTHERS   = 1.
  IF l_got_state <> 'A'.
    MESSAGE 'Table does not exist' TYPE 'E'.
  ENDIF.
  IF p_table+0(1) <> 'Z' AND
     p_table+0(1) <> 'Y'.
    MESSAGE 'Please use only Z or Y tables.' TYPE 'E'.
  ENDIF.

*----------------------------------------------------------------------*
*     AT SELECTION-SCREEN ON VALUE-REQUEST
*----------------------------------------------------------------------*
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.

  CALL FUNCTION 'F4_FILENAME'
    IMPORTING
      file_name = p_file.

START-OF-SELECTION.

* Dynamically create internal table
  CREATE DATA lt_table_data TYPE TABLE OF (p_table).
  ASSIGN lt_table_data->* TO <lt_table_data>.

  CREATE DATA new_line LIKE LINE OF <lt_table_data>.
  ASSIGN new_line->* TO <ls_table_data>.

* Generate field catalog for the table
  CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
    EXPORTING
      i_structure_name = p_table
    CHANGING
      ct_fieldcat      = lt_fieldcat
    EXCEPTIONS
      OTHERS           = 1.
  IF sy-subrc <> 0.
    MESSAGE 'Error generating field catalog' TYPE 'E'.
  ENDIF.

  lv_filename = p_file.
* Read file into internal table
  CALL FUNCTION 'GUI_UPLOAD'
    EXPORTING
      filename                = lv_filename
      filetype                = 'ASC'
    TABLES
      data_tab                = lt_file_data
    EXCEPTIONS
      file_open_error         = 1
      file_read_error         = 2
      no_batch                = 3
      gui_refuse_filetransfer = 4
      OTHERS                  = 5.
  IF sy-subrc <> 0.
    MESSAGE 'Error uploading file' TYPE 'E'.
  ENDIF.

* Delete all entries from the target table
  DELETE FROM (p_table).
  IF sy-subrc = 0.
    MESSAGE 'All entries deleted from table' TYPE 'I'.
  ENDIF.

* Parse and insert data into the table

  DESCRIBE TABLE lt_file_data LINES DATA(lv_idx).
  DELETE lt_file_data INDEX lv_idx.

  LOOP AT lt_file_data INTO DATA(ls_line) FROM 4.
    CLEAR <ls_table_data>.

    LOOP AT lt_fieldcat ASSIGNING FIELD-SYMBOL(<fs_fieldcat>).
      IF <fs_fieldcat>-fieldname = 'MANDT'.
        ASSIGN COMPONENT <fs_fieldcat>-fieldname OF STRUCTURE <ls_table_data> TO <lv_field>.
        IF sy-subrc = 0.
          <lv_field> = sy-mandt.
        ENDIF.
      ELSE.
        CLEAR lv_offset.
        DO strlen( ls_line ) TIMES.
          DATA(lv_index) = sy-index.

          DATA(lv_single) = substring( val = ls_line off = lv_index - 1 len = 1 ).
          IF lv_index = 1 AND lv_single = '|'.
            lv_offset = lv_offset + 1.
          ELSEIF lv_single = '|'.   "New field
            lv_until = lv_index - lv_offset - 1.
            lv_field = ls_line+lv_offset(lv_until).
            lv_offset = lv_offset + lv_until + 1.

            ASSIGN COMPONENT <fs_fieldcat>-fieldname OF STRUCTURE <ls_table_data> TO <lv_field>.
            IF sy-subrc = 0.
              <lv_field> = lv_field.
            ENDIF.
            ls_line = ls_line+lv_offset.
            EXIT.
          ENDIF.
        ENDDO.
      ENDIF.
    ENDLOOP.

    APPEND <ls_table_data> TO <lt_table_data>.

  ENDLOOP.

* Insert data into the database table
  INSERT (p_table) FROM TABLE <lt_table_data>.
  IF sy-subrc = 0.
    MESSAGE 'Data successfully inserted into table' TYPE 'S'.
  ELSE.
    MESSAGE 'Error inserting data into table' TYPE 'E'.
  ENDIF.

SE39 ABAP split screen editor

The SE39 transaction is used to compare two ABAP’s in split screen mode. This can be two ABAP programs in single system, or by clicking on the button Compare Different Systems between two systems. With the cross system comparison the tool will ask for which RFC to use.

Usage of SE39

Start transaction SE39. In this example we will take a copy of a standard SAP BAPI and compare it with the original (to find clones, read this blog):

Press display to compare. Initially not much happens, except source code on the left and right.

Press the Comparison On button:

Result now shows the delta’s with indicators on the left side of the screen:

Use the buttons Next difference from cursor and the other button to quick jump through the code to find the differences.

References

Bug fix notes:

3539141 – Splitscreeneditor: Error for empty source in version management

3564002 – SE39: improve handling of inactive programs

3568037 – Enhancements are not being displayed in SE39 for remote systems

XPRA transport actions

XPRA actions can be a must to to trigger actions after import. The most know is to trigger the RV80HGEN program jointly with update of requirement or formula.

The XPRA can also be misused by hackers to execute actions right after transport import. Since XPRA is done with the DDIC user upon import, it will take the rights from DDIC as well. Very often DDIC has SAP_ALL assigned.

Correct usage of XPRA

To add a XPRA action, go to the transport via SE10 or SE01 and press change. Then use the Insert Row button to manually add the XPRA action:

Add program ID R3TR, object type XPRA and object name the program to be executed. In this case the RV80GHEN program.

Now save the transport.

Upon import the program will be executed at the end of the import. This can be seen in the transport import log details:

Other XPRA programs

Next to RV80GHEN there are more known XPRA programs:

2400599 – List of Programs to regenerate CUSTOM fields after upgrade or patch update lists RGUGBR02, SAPFACCG and RFBIBLG0.

1365712 – Generating FI programs during posting after importing an SP lists program RGZZGLUX to regenerate the finance programs.

1855686 – Dump in XPRA phase because of FAGL_VIEWS_GENERATE_ALL lists program SAPLFAGL_VIEWS_GENERATE (and old program SAPFACCG).

Misuse of XPRA

Off course any developer can add a malicious XPRA statement to execute a program upon import.

What to do about this?

Prevention is not possible. Detection is possible. See the blog on adding a critical object check to the transport check tool. In this case add a check on R3TR XPRA and program *. This will detect any XPRA object.

Webdynpro tips & tricks

SAP web dynpro is still in use and is a good way for ABAP devevloper to create web screens for end users.

Tips and tricks for basis

Transactions:

  • SICF: activation or de-activation of web dynpro services (see blog for all SICF tips and tricks)
  • SICF_SESSIONS: Enable HTTP security session management at backend(ABAP) system
  • SMGW: gateway monitor
  • SMICM: HTTPs settings

General:

SAP help documentation on WebDynpro can be found here (very extensive).

Unified rendering

Web dynpro uses the unified rendering framework. This needs regular patching. See this blog for more information.

SCOV coverage analyzer

The SAP coverage analyze can be used in testing custom code to make sure all testing covers all code and all branches.

The coverage analyzer has quite an in-depth analysis capability that is meant for non-productive systems. Don’t run it on productive systems.

Starting the coverage analyzer

On your test system start transaction SCOV and go to the on/off section in the Administration branch:

When switched on it will take at least 1 hour before the first results are visible.

Analyzing the results

During the testing you can view the test coverage results in the Display/Details section. Select there the Z* for all the custom code (you might need to increase the Maximum Number of Objects in the Settings tab):

Wait for the result list and sort by Coverage:

Double clicking on one of the lines gives the coverage per program:

If you select any line you jump to the code lines:

In this case (yellow one), you can see that the normal flow is done, but the exceptions are not reached yet. If the exceptions are important for your business scenario, you know you have to create and fire a test case. Once done, the coverage should increase.

Overall view

In the overall view you can see trend analysis over time:

To refresh the analysis or to set up first time push the Update Analysis button in the middle section (it is a bit hidden).

References

Standard SAP reference for Coverage analyzer: link.

OSS notes:

ABAP dictionary check

In some cases you need to check the entire system for inactive objects.

For this you can use program RUTMSJOB (transaction S_SLN_44000018):

Select the checks and schedule the jobs with the button.

With the job overview you can see the running status:

When completed you can use the Results button:

By clicking on the glass icon, you can list the results of items that are not ok.

Other potential programs

Other potential programs are these ones:

DD_REPAIR_TABT_DBPROPERTIES, see OSS notes

RUTCONSCHECK and RUTCHKNT, see OSS note:

OAUTH call from ABAP

OAUTH can be called from custom ABAP. The explanation is given in this formal SAP help file. But it is quite complex.

In the example program below we will use OAUTH to call SAP BTP CPI.

First in SE80 we create a OAUTH client profile named ZOAUTH_CLIENT_PROFILE_CPI:

Then the rest of the ABAP coding is according to the SAP help file, including the error handling on issues you might face.

*&--------------------------------------------------------------------*
*& Report Z_CALL_API_USING_OAUTH
*&--------------------------------------------------------------------*
*&
*&--------------------------------------------------------------------*

REPORT z_call_api_using_oauth.

PARAMETERS:
  zp_url   TYPE string                    LOWER CASE
                                          DEFAULT 'https://apimanagement.eu10.hana.ondemand.com/v1/api/hc/xxx/call_name',
  zp_sslid TYPE strustssl-applic          DEFAULT 'ANONYM',
  zp_profl TYPE oa2c_profiles-profile     DEFAULT 'ZOAUTH_CLIENT_PROFILE_CPI',
  zp_confg TYPE oa2c_client-configuration DEFAULT 'ZOAUTH_CLIENT_PROFILE_CPI'.

CONSTANTS:
  BEGIN OF zgcs_create_return,
    argument_not_found TYPE sy-subrc VALUE 1,
    plugin_not_active  TYPE sy-subrc VALUE 2,
    internal_error     TYPE sy-subrc VALUE 3,
    others             TYPE sy-subrc VALUE 4,
  END OF zgcs_create_return.

START-OF-SELECTION.

  " oData: restrict to two entries returned, via url
  DATA(zgv_api_url) = |{ zp_url }?$top=2|.

  cl_http_client=>create_by_url( EXPORTING  url                = zgv_api_url
                                            ssl_id             = zp_sslid
                                 IMPORTING  client             = DATA(zlo_http_client)
                                 EXCEPTIONS argument_not_found = zgcs_create_return-argument_not_found
                                            plugin_not_active  = zgcs_create_return-plugin_not_active
                                            internal_error     = zgcs_create_return-internal_error
                                            OTHERS             = zgcs_create_return-others ).

  CASE sy-subrc.
    WHEN zgcs_create_return-argument_not_found.
      MESSAGE 'Argument not found when trying to create http client instance' TYPE 'E'.
    WHEN zgcs_create_return-plugin_not_active.
      MESSAGE 'Plugin not active for creation of http client instance' TYPE 'E'.
    WHEN zgcs_create_return-internal_error.
      MESSAGE 'Internal error when trying to create http client instance' TYPE 'E'.
    WHEN zgcs_create_return-others.
      MESSAGE 'Generic error when trying to create http client instance' TYPE 'E'.
  ENDCASE.

  zlo_http_client->propertytype_logon_popup = zlo_http_client->co_disabled.

  TRY.
      DATA(zgo_oauth_client) = cl_oauth2_client=>create( i_profile       = zp_profl
                                                         i_configuration = zp_confg ).
    CATCH cx_oa2c_config_not_found.
      MESSAGE 'OAuth 2.0 Client Configuration not found' TYPE 'E'.
    CATCH cx_oa2c_config_profile_assign.
      MESSAGE 'OAuth 2.0 Client Config - Unassigned Profile' TYPE 'E'.
    CATCH cx_oa2c_kernel_too_old.
      MESSAGE 'OAuth 2.0 Client - Kernel too old' TYPE 'E'.
    CATCH cx_oa2c_missing_authorization.
      MESSAGE 'OAuth 2.0 Client missing authorization' TYPE 'E'.
    CATCH cx_oa2c_config_profile_multi.
      MESSAGE 'OAuth 2.0 Client Config - Profile assigned multiple times' TYPE 'E'.
  ENDTRY.

  " Set oAuth token to the http client
  TRY.
      zgo_oauth_client->set_token( io_http_client = zlo_http_client
                                   i_param_kind   = if_oauth2_client=>c_param_kind_header_field ).
    CATCH cx_oa2c_at_not_available
          cx_oa2c_at_expired.

      " When setting the token fails, first try and get a new token
      TRY.
          zgo_oauth_client->execute_cc_flow( ).
        CATCH cx_oa2c_badi_implementation.
          MESSAGE 'OAuth 2.0 Client BAdI Impl. Error' TYPE 'E'.
        CATCH cx_oa2c_not_supported.
          MESSAGE 'Not supported by Service Provider.' TYPE 'E'.
        CATCH cx_oa2c_not_allowed.
          MESSAGE 'OAuth 2.0 Client Runtime - Not Allowed' TYPE 'E'.
        CATCH cx_oa2c_prot_http_failure.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - HTTP Failure' TYPE 'E'.
        CATCH cx_oa2c_prot_other_error.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Other Error' TYPE 'E'.
        CATCH cx_oa2c_prot_unexpected_code.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Unexpected Code' TYPE 'E'.
        CATCH cx_oa2c_prot_http_forbidden.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - HTTP 403 - Forbidden' TYPE 'E'.
        CATCH cx_oa2c_prot_http_not_found.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - HTTP 404 - Not Found' TYPE 'E'.
        CATCH cx_oa2c_server_error.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Server Error' TYPE 'E'.
        CATCH cx_oa2c_temporarily_unavail.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Temporarily Unavailable' TYPE 'E'.
        CATCH cx_oa2c_unsupported_grant_type.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Unsupported Grant Type' TYPE 'E'.
        CATCH cx_oa2c_unauthorized_client.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Unauthorized Client' TYPE 'E'.
        CATCH cx_oa2c_invalid_scope.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Scope' TYPE 'E'.
        CATCH cx_oa2c_invalid_grant.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Grant' TYPE 'E'.
        CATCH cx_oa2c_invalid_client.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Client' TYPE 'E'.
        CATCH cx_oa2c_invalid_request.
          MESSAGE 'OAuth 2.0 Client Runtime Protocol - Invalid Request' TYPE 'E'.
        CATCH cx_oa2c_invalid_parameters.
          MESSAGE 'OAuth 2.0 Client Runtime - Invalid Parameters' TYPE 'E'.
        CATCH cx_oa2c_secstore_adm.
          MESSAGE 'OAuth 2.0 Client Runtime - SecStore Administration' TYPE 'E'.
        CATCH cx_oa2c_secstore.
          MESSAGE 'OAuth 2.0 Client Runtime - Secstore' TYPE 'E'.
        CATCH cx_oa2c_protocol_exception.
          MESSAGE 'OAuth 2.0 Client Runtime - Protocol Exception' TYPE 'E'.
      ENDTRY.

      " Set oAuth token to the http client
      TRY.
          zgo_oauth_client->set_token( io_http_client = zlo_http_client
                                       i_param_kind   = if_oauth2_client=>c_param_kind_header_field ).
        CATCH cx_oa2c_at_not_available.
          MESSAGE 'oAuth 2.0: Acces token not available' TYPE 'E'.
        CATCH cx_oa2c_at_expired.
          MESSAGE 'Access Token has expired.' TYPE 'E'.
        CATCH cx_oa2c_at_profile_not_covered.
          MESSAGE 'Access token has expired.' TYPE 'E'.
        CATCH cx_oa2c_not_supported.
          MESSAGE 'Not supported by Service Provider.' TYPE 'E'.
        CATCH cx_oa2c_badi_implementation.
          MESSAGE 'OAuth 2.0 Client BAdI Impl. Error' TYPE 'E'.
        CATCH cx_oa2c_secstore.
          MESSAGE 'OAuth 2.0 Client Runtime - Secstore' TYPE 'E'.
        CATCH cx_oa2c_invalid_parameters.
          MESSAGE 'OAuth 2.0 Client Runtime - Invalid Parameters' TYPE 'E'.
        CATCH cx_oa2c_icf_error.
          MESSAGE 'Unknown error received from ICF.' TYPE 'E'.
      ENDTRY.

    CATCH cx_oa2c_at_profile_not_covered.
      MESSAGE 'Access token has expired.' TYPE 'E'.
    CATCH cx_oa2c_not_supported.
      MESSAGE 'Not supported by Service Provider.' TYPE 'E'.
    CATCH cx_oa2c_badi_implementation.
      MESSAGE 'OAuth 2.0 Client BAdI Impl. Error' TYPE 'E'.
    CATCH cx_oa2c_secstore.
      MESSAGE 'OAuth 2.0 Client Runtime - Secstore' TYPE 'E'.
    CATCH cx_oa2c_invalid_parameters.
      MESSAGE 'OAuth 2.0 Client Runtime - Invalid Parameters' TYPE 'E'.
    CATCH cx_oa2c_icf_error.
      MESSAGE 'Unknown error received from ICF.' TYPE 'E'.
  ENDTRY.

  " From here on handle the http client for the API interaction
  zlo_http_client->request->set_version( if_http_request=>co_protocol_version_1_0 ).
  DATA(zlo_rest_client) = NEW cl_rest_http_client( io_http_client = zlo_http_client ).

" Get data from API
  TRY.
      zlo_rest_client->if_rest_client~get( ).
      " Collect response received from the REST API
      DATA(zli_response) = zlo_rest_client->if_rest_client~get_response_entity( ).
      DATA(zgv_http_status_code) = zli_response->get_header_field( `~status_code` ).
      DATA(zgv_status_reason)    = zli_response->get_header_field( `~status_reason` ).
      DATA(zgv_response_data)    = zli_response->get_string_data( ).

      " Record the response of the interface
      IF zgv_http_status_code BETWEEN 200 AND 299.
        " Success
        MESSAGE 'Call was succesful' TYPE 'S'.
      ELSE.
        MESSAGE 'Call failed' TYPE 'E'.
      ENDIF.

      WRITE / 'Response'.
      WRITE / zgv_response_data.

      " Issues with REST client must not lead to a short-dump
    CATCH cx_rest_client_exception INTO DATA(zlx_rest_client).
      IF zlx_rest_client->if_t100_message~t100key IS NOT INITIAL.
        DATA zlv_message TYPE string.
        MESSAGE ID zlx_rest_client->if_t100_message~t100key-msgid
                 TYPE 'E'
                 NUMBER zlx_rest_client->if_t100_message~t100key-msgno
                   WITH zlx_rest_client->if_t100_message~t100key-attr1
                        zlx_rest_client->if_t100_message~t100key-attr2
                        zlx_rest_client->if_t100_message~t100key-attr3
                        zlx_rest_client->if_t100_message~t100key-attr4.
      ELSE.
        MESSAGE 'Rest client Exception' TYPE 'E'.
      ENDIF.
  ENDTRY.

  zlo_http_client->close( ).

Security Services Tools

SAP offers on GitHub some extra Security Service Tools. These are custom Z ABAPs you can download and modify to your needs.

Link to GitHub:

GitHub – SAP-samples/security-services-tools: If you use security-related services and tools such as EWA, SOS, System Recommendations, Configuration Validation, or a security dashboard in SAP Solution Manager, the ABAP reports in this repository can help with further analysis and development.

Interesting programs from Security Service Tools

Some highlights from the Security Service Tools page:

  • Extensive cleanup program for weak hashes (including the password history data)
  • Workload statistics of RFC calls
  • Show SNC status of active users on application server
  • Show RFC gateway and logging settings
  • History of dynamic profile parameters
  • ….
  • Many more