This is a test message to test the length of the message box.
Login
ABAP Cloud Migration Frontend
Created by Software-Heroes

ABAP Cloud - Migration (Frontend)

1705

How do you migrate a classic report to ABAP Cloud? In this article we look at an example of a migration.



In a previous example, we looked at the migration of a report as an application job. In many cases, however, ALV reports are also used, which the user works with. In this article, we will look at the migration of such an ALV report towards Fiori Elements.

 

Introduction

For many years, SAP GUI was the standard for working with an SAP system. With ABAP Cloud, the SAP GUI is no longer supported and more and more applications are being implemented using Fiori. As an ABAP developer, you should now look at how you can implement the classic report as a Fiori application and how the elements behave. Not everything will work the same as in the GUI, so the concepts should be adapted to Fiori.

The implementation was done on an S/4 HANA 2022 system (Cloud Appliance Library). You can find the data and configurations shown there if you want to recreate our example.

 

Preparation

In this section you will get all the information about the actual report, how it is structured and how it works. In the report we want to use a selection screen to delimit FI documents and display the headers, together with the items and texts. The user then has the option of adding comments to the individual items, which is the added value of the report.

 

Table

The comments on the document items are stored in an additional table, the table contains the complete key, the comment and some control information about the user.

@EndUserText.label : 'Comments for FI documents'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zfi_comment {
  key client  : abap.clnt not null;
  key bukrs   : bukrs not null;
  key belnr   : belnr_d not null;
  key gjahr   : gjahr not null;
  key buzei   : buzei not null;
  commt       : abap.string(0);
  create_user : xubname;
  create_time : abap.utclong;
}

 

Report

Here you will find the report resources. In the report there is a selection screen, a screen which we will use as a popup. To simplify things, we have implemented the logic in a local class in the report.

REPORT zfi_comment_bookings.

TABLES bkpf.

" -----------------------------------------------------------------------
" --- Selection Screen
" -----------------------------------------------------------------------
SELECT-OPTIONS:
  s_bukrs FOR bkpf-bukrs,
  s_belnr FOR bkpf-belnr,
  s_gjahr FOR bkpf-gjahr.
PARAMETERS p_numbr TYPE i DEFAULT 50.

" -----------------------------------------------------------------------
" --- Selection Subscreen
" -----------------------------------------------------------------------
SELECTION-SCREEN BEGIN OF SCREEN 0100.
  PARAMETERS p_commt TYPE string LOWER CASE.
SELECTION-SCREEN END OF SCREEN 0100.

" -----------------------------------------------------------------------
" --- Report class
" -----------------------------------------------------------------------
CLASS lcl_report DEFINITION FINAL.
  PUBLIC SECTION.
    TYPES: BEGIN OF ts_selection,
             s_bukrs TYPE RANGE OF bkpf-bukrs,
             s_belnr TYPE RANGE OF bkpf-belnr,
             s_gjahr TYPE RANGE OF bkpf-gjahr,
             p_numbr TYPE i,
           END OF ts_selection.

    TYPES: BEGIN OF ts_data,
             bukrs TYPE bkpf-bukrs,
             butxt TYPE t001-butxt,
             belnr TYPE bkpf-belnr,
             gjahr TYPE bkpf-gjahr,
             buzei TYPE bseg-buzei,
             bldat TYPE bkpf-bldat,
             bktxt TYPE bkpf-bktxt,
             bschl TYPE bseg-bschl,
             sgtxt TYPE bseg-sgtxt,
             kostl TYPE bseg-kostl,
             commt TYPE string,
           END OF ts_data.
    TYPES tt_data TYPE STANDARD TABLE OF ts_data WITH EMPTY KEY.

    METHODS main
      IMPORTING is_selection TYPE ts_selection.

  PRIVATE SECTION.
    DATA ms_selection TYPE ts_selection.
    DATA mt_data      TYPE tt_data.
    DATA mo_salv      TYPE REF TO cl_salv_table.

    METHODS select_data.

    METHODS show_alv.

    METHODS on_double_click
      FOR EVENT double_click OF cl_salv_events_table
      IMPORTING !row
                !column.

    METHODS save_comment
      IMPORTING is_data    TYPE ts_data
                id_comment TYPE string.
ENDCLASS.


CLASS lcl_report IMPLEMENTATION.
  METHOD main.
    ms_selection = is_selection.

    select_data( ).
    show_alv( ).
  ENDMETHOD.


  METHOD select_data.
    SELECT
      FROM bkpf AS h
             INNER JOIN
               bseg AS p ON p~bukrs = h~bukrs AND p~belnr = h~belnr AND p~gjahr = h~gjahr
                 INNER JOIN
                   t001 AS c ON c~bukrs = h~bukrs
                     LEFT OUTER JOIN
                       zfi_comment AS z ON z~bukrs = h~bukrs AND z~belnr = h~belnr AND z~gjahr = h~gjahr AND z~buzei = p~buzei
      FIELDS h~bukrs,
             c~butxt,
             h~belnr,
             h~gjahr,
             p~buzei,
             h~bldat,
             h~bktxt,
             p~bschl,
             p~sgtxt,
             p~kostl,
             z~commt
      WHERE     h~bukrs IN @ms_selection-s_bukrs
            AND h~belnr IN @ms_selection-s_belnr
            AND h~gjahr IN @ms_selection-s_gjahr
      INTO CORRESPONDING FIELDS OF TABLE @mt_data
      UP TO @ms_selection-p_numbr ROWS.
  ENDMETHOD.


  METHOD show_alv.
    TRY.
        cl_salv_table=>factory( IMPORTING r_salv_table = mo_salv
                                CHANGING  t_table      = mt_data ).

        DATA(lo_functions) = mo_salv->get_functions( ).
        lo_functions->set_all( ).

        DATA(lo_display) = mo_salv->get_display_settings( ).
        lo_display->set_striped_pattern( abap_true ).

        DATA(lo_events) = mo_salv->get_event( ).
        SET HANDLER on_double_click FOR lo_events.

        mo_salv->display( ).

      CATCH cx_salv_msg.
    ENDTRY.
  ENDMETHOD.


  METHOD on_double_click.
    TRY.
        DATA(lr_data) = REF #( mt_data[ row ] ).

      CATCH cx_sy_itab_line_not_found.
        RETURN.
    ENDTRY.

    CALL SELECTION-SCREEN 0100 STARTING AT 10 10.
    IF p_commt IS INITIAL.
      RETURN.
    ENDIF.

    lr_data->commt = p_commt.

    save_comment( is_data    = lr_data->*
                  id_comment = p_commt ).

    mo_salv->refresh( ).
  ENDMETHOD.


  METHOD save_comment.
    DATA(ls_comment) = CORRESPONDING zfi_comment( is_data ).
    ls_comment-commt       = id_comment.
    ls_comment-create_user = sy-uname.
    ls_comment-create_time = utclong_current( ).

    INSERT zfi_comment FROM @ls_comment.
    IF sy-subrc <> 0.
      UPDATE zfi_comment FROM @ls_comment.
    ENDIF.

    COMMIT WORK.
  ENDMETHOD.
ENDCLASS.

" -----------------------------------------------------------------------
" --- Report logic
" -----------------------------------------------------------------------

INITIALIZATION.
  DATA(go_app) = NEW lcl_report( ).

START-OF-SELECTION.
  go_app->main( VALUE #( s_bukrs = s_bukrs[]
                         s_belnr = s_belnr[]
                         s_gjahr = s_gjahr[]
                         p_numbr = p_numbr ) ).

 

Usage

When we start the report, we get the selection screen, here we can restrict the documents that we want to see and edit immediately.

 

After executing the selection, we get the result displayed as an ALV and can check the various data that is displayed.

 

If we double-click on an entry, a corresponding popup opens and we can create a comment for this line. The comment is saved directly in the database and displayed in a column.

 

Conversion

Let's start by creating our Fiori application. We will move all components that we can and need in ABAP Cloud to the software component.

 

Step 1 - Software Component

Creating a new software component in the ABAP Cloud language version. We then create a structure package with the software component in order to be able to create our application.

 

Under the structure package we create the "Development" package ZFI_T1_COMMENT_APP, in which we will now develop our application.

 

Step 2 - Customer-specific table

Before we migrate the data model, we want to move our existing table ZFI_COMMENT to our new software component. To do this, we first have to change the language version to "ABAP for Cloud Development" change.

 

When activating, we will receive an error message that the data element XUBNAME is not released. All other data elements from the standard are already released.

 

The domain XUBNAME is, however, released and after some research into the Cloudification Repository or the released data elements, we find USNAM. We replace the data element and activate the table. Now we can move the activated table into our software component.

 

Step 3 - Data model

First, we would start by setting up the data model, as this was a central part of the evaluation. In this case, however, we cannot build on the tables, but need the released Core Data Services for the tables. Here you have the option of searching for the released objects using the Cloudification Repository Viewer (SAP or SwH).

  • BKPF - I_JournalEntry
  • BSEG - I_OperationalAcctgDocItem
  • T001 - I_CompanyCode

 

Instead of accessing at the ABAP level, we would now model a Core Data Service, which we then need as the basis for our RAP application. In the first step, we create a CDS view for the comment table and normalize the field names for the data model.

@EndUserText.label: 'Comments to FI documents'
define view entity ZFI_I_Comment
  as select from zfi_comment
{
  key bukrs       as CompanyCode,
  key belnr       as AccountingDocument,
  key gjahr       as FiscalYear,
  key buzei       as AccountingDocumentItem,
      commt       as DocumentComment,
      create_user as CreationUser,
      create_time as CreationTime
}

 

Now we can model our actual data view. Since we are working with the position data, we would build on the positions and gather the data using the associations. For the comments, we need to define an additional association in our view. Since we want to implement a behavior later, you need a ROOT entity.

@EndUserText.label: 'Datasource'
@Metadata.allowExtensions: true
define root view entity ZFI_I_DocumentComment
  as select from I_OperationalAcctgDocItem
  association [0..1] to ZFI_I_Comment as _Comment on  _Comment.CompanyCode            = $projection.CompanyCode
                                                  and _Comment.AccountingDocument     = $projection.AccountingDocument
                                                  and _Comment.FiscalYear             = $projection.FiscalYear
                                                  and _Comment.AccountingDocumentItem = $projection.AccountingDocumentItem
{
  key CompanyCode,
  key AccountingDocument,
  key FiscalYear,
  key AccountingDocumentItem,
      _CompanyCode.CompanyCodeName,
      _JournalEntry.DocumentDate,
      _JournalEntry.AccountingDocumentHeaderText,
      PostingKey,
      DocumentItemText,
      CostCenter,
      _Comment.DocumentComment
}

 

If we look at the view in the "Dependency Analyzer", our model looks like this in the "SQL Dependency Graph" tab.

 

Step 4 - Application

To get a first running application, we create a service definition on the Core Data Service and thereby release the view in the service.

@EndUserText.label: 'Comment App'
define service ZFI_COMMENT_APP {
  expose ZFI_I_DocumentComment as DocumentComment;
}

 

Now we create a service binding of type UI for OData v4 and activate the endpoint in the system via "Publish" or on-premise via transaction /IWFND/V4_ADMIN.

 

If we now look at the current status of the application, some columns and the filters for the application are still missing. We can adapt the UI accordingly using the settings (gear wheel) and "Adapt Filters". However, the adjustments are only available to us temporarily.

 

To ensure that the Fiori Elements app meets our requirements, we create a metadata extension so that we can influence the order of the fields and define fields on the filter bar. 

@Metadata.layer: #CUSTOMER
annotate entity ZFI_I_DocumentComment with
{

  @UI            : {
    selectionField: [{ position: 10 }],
    lineItem   : [
      { position: 10 },
      { type: #FOR_ACTION, dataAction: 'setComment',label: 'Set comment' } ]
  }
  CompanyCode;

  @UI            : {
    selectionField: [{ position: 20 }],
    lineItem   : [{ position: 30 }]
  }
  AccountingDocument;

  @UI            : {
    selectionField: [{ position: 30 }],
    lineItem   : [{ position: 40 }]
  }
  FiscalYear;

  @UI            : {
    lineItem   : [{ position: 50 }]
  }
  AccountingDocumentItem;

  @UI            : {
    lineItem   : [{ position: 20 }]
  }
  CompanyCodeName;

  @UI            : {
    lineItem   : [{ position: 60 }]
  }
  DocumentDate;

  @UI            : {
    lineItem   : [{ position: 70 }]
  }
  AccountingDocumentHeaderText;

  @UI            : {
    lineItem   : [{ position: 80 }]
  }
  PostingKey;

  @UI            : {
    lineItem   : [{ position: 90 }]
  }
  DocumentItemText;

  @UI            : {
    lineItem   : [{ position: 100 }]
  }
  CostCenter;

  @UI            : {
    lineItem   : [{ position: 110 }]
  }
  DocumentComment;
}

 

If you now update the preview of the application again, you will receive the final application with all filters and columns, as the ALV had already shown it.

 

Step 5 - Action

In the report we still had a double-click event on the ALV, such an event is not standard in Fiori, so we are implementing an alternative. Since the double-click is tied to an ALV entry, we need a normal ACTION and not a STATIC ACTION. Since we want a popup for the input, we create a corresponding structure in the form of an abstract entity.

@EndUserText.label: 'Popup for comment'
define abstract entity ZFI_S_CommentPopUp
{
  @EndUserText.label: 'Comment'
  DocumentComment : abap.string;
}

 

Next, we define a behavior definition on our root view. Here, we create a MANAGED implementation and use an UNMANAGED SAVE, since we do not have a table in the background for the data. We also define an action to add our comments. We need the RESULT so that the data record is updated in the UI after the action has been executed.

managed implementation in class zbp_fi_document_comment unique;
strict ( 2 );

define behavior for ZFI_I_DocumentComment alias DocumentComment
with unmanaged save
lock master
authorization master ( instance )
{
  action setComment parameter ZFI_S_CommentPopUp result [1] $self;
}

 

Using CTRL + 1 we then create the class and implement the action and saving in our additional table.

CLASS lcl_buffer DEFINITION.
  PUBLIC SECTION.
    TYPES tt_comment TYPE STANDARD TABLE OF zfi_comment WITH EMPTY KEY.

    CLASS-DATA gt_comment TYPE tt_comment.
ENDCLASS.


CLASS lhc_DocumentComment DEFINITION INHERITING FROM cl_abap_behavior_handler.
  PRIVATE SECTION.
    METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION
      IMPORTING keys REQUEST requested_authorizations FOR DocumentComment RESULT result.

    METHODS setComment FOR MODIFY
      IMPORTING keys FOR ACTION DocumentComment~setComment RESULT result.
ENDCLASS.


CLASS lhc_DocumentComment IMPLEMENTATION.
  METHOD get_instance_authorizations.
  ENDMETHOD.


  METHOD setComment.
    READ ENTITIES OF ZFI_I_DocumentComment IN LOCAL MODE
         ENTITY DocumentComment ALL FIELDS WITH CORRESPONDING #( keys )
         RESULT DATA(lt_data).

    LOOP AT lt_data INTO DATA(ls_data).
      DATA(ls_key) = keys[ %tky = ls_data-%tky ].

      INSERT VALUE #( bukrs       = ls_key-CompanyCode
                      belnr       = ls_key-AccountingDocument
                      gjahr       = ls_key-FiscalYear
                      buzei       = ls_key-AccountingDocumentItem
                      commt       = ls_key-%param-DocumentComment
                      create_user = cl_abap_context_info=>get_user_technical_name( )
                      create_time = utclong_current( ) )
             INTO TABLE lcl_buffer=>gt_comment.

      INSERT VALUE #( %tky   = ls_key-%tky
                      %param = CORRESPONDING #( ls_data ) )
             INTO TABLE result REFERENCE INTO DATA(lr_document).

      lr_document->%param-DocumentComment = ls_key-%param-DocumentComment.
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.


CLASS lsc_ZFI_I_DOCUMENTCOMMENT DEFINITION INHERITING FROM cl_abap_behavior_saver.
  PROTECTED SECTION.
    METHODS
      save_modified REDEFINITION.

    METHODS
      cleanup_finalize REDEFINITION.

ENDCLASS.


CLASS lsc_ZFI_I_DOCUMENTCOMMENT IMPLEMENTATION.
  METHOD save_modified.
    LOOP AT lcl_buffer=>gt_comment INTO DATA(ls_comment).
      INSERT zfi_comment FROM @ls_comment.
      IF sy-subrc <> 0.
        UPDATE zfi_comment FROM @ls_comment.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.


  METHOD cleanup_finalize.
  ENDMETHOD.
ENDCLASS.

 

What exactly have we implemented in the action?

  • In the SETCOMMENT method we read the selected data records
  • Processing the data records
  • KEYS contains the structure (%param) for passing the comment
  • Filling the buffer for creating/changing the data records in the SAVE sequence
  • Returning the updated data record to refresh the application
  • Saving the data records in SAVE_MODIFIED

 

We have already implemented the action with the metadata extension and displayed it on the UI. If we now mark an entry in the list and trigger the action, a corresponding pop-up appears for entering the comment.

 

We can then test the action, regardless of whether it is for one or more data records. The comment is applied to the marked document lines. This means we have covered an additional requirement without much effort, which was not initially possible with the double click in the ALV.

 

Comparison

Finally, we will compare the converted features of the Fiori Elements application with the classic report. Not all functions can always be transferred one-to-one to an Elements application, as the concepts are not the same. So let's start with the selection screen, which you can find in the Fiori Elements application as a filter bar in the upper area. The advantage of Fiori is that the user can add additional fields for filtering.

 

The ALV output is displayed as a "responsive table" under the filter bar. Alternatively, you can create a "grid table" when generating so that you can see all the columns next to each other. The advantage of the Fiori app is that you can also jump to the object page via the list in order to display all the information in a structured format.

 

Finally, a comparison of the two actions. Although we cannot trigger the popup by double-clicking, we can select multiple entries and enter a comment for them. However, this would also be possible with a little more effort in the ALV output.

 

Best Practice

Today's example is primarily intended to demonstrate a simple migration of the application towards the ABAP Cloud, but some best practices that would normally be present are not taken into account.

  • Projection - Normally you would add a projection layer over the business object that exposes the app and the functions to the outside.
  • Managed - In the managed scenario we can use the transactional buffer and do not have to use our own buffer object.
  • Authorizations - In the CDS View ZFI_I_DocumentComment, an authorization check should be carried out on the documents to be displayed. The authorization to enter and change comments would also have to be implemented.

 

Summary

Here is a brief summary of the steps taken in today's article.

  • Installing the software component in the system
  • Migrating the additional table to ABAP Cloud
  • Structuring the data model
  • Creating the application
  • Implementing the action (behavior)

 

The following objects have now been created from the report and the additional tables in order to create our Fiori Elements application. However, the deployed application is still missing in the system; here we have only worked with the preview of the application.

 

Conclusion

Creating a simple application for displaying data is very easy and does not require many additional objects, as we do not necessarily have to create the entire RAP stack. Most of the report's functions can be applied to a Fiori Elements app, but the app must also take the new concepts into account.


Included topics:
ABAP CloudABAPMigrationBeispiel
Comments (2)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


ABAP Cloud - HTTP Client

Category - ABAP

What does the current HTTP client in ABAP Cloud look like? Let's take a look at the new model.

11/01/2024

ABAP Cloud - Key User Apps

Category - ABAP

Key User Extensibility is a part of ABAP Cloud when it comes to extending the core, but how can you use the tools effectively and how do they complement each other?

10/11/2024

ABAP Cloud - JSON Conversion

Category - ABAP

Is there a new API for converting JSON and do you need it for ABAP Cloud? Here we will take a look at the structure and conversion of JSON.

09/27/2024

ABAP Cloud - Number ranges

Category - ABAP

How can you use number ranges in ABAP Cloud and does this actually still make sense? In this article we will look at some of the background informations.

09/13/2024

ABAP Cloud - Data Types

Category - ABAP

What does it actually look like when using data elements in ABAP Cloud? In this article we look at alternatives in the definition.

09/06/2024