Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[ABAP] LOOP AT VALUE

Easy way to loop over multiple tables of the same type, without having to create an additional table to join the data before the loop.

SELECT FROM spfli FIELDS * WHERE carrid EQ 'AA' INTO TABLE @DATA(lt_spfli_1).

SELECT FROM spfli FIELDS * WHERE carrid EQ 'AZ' INTO TABLE @DATA(lt_spfli_2).

LOOP AT VALUE spfli_tab( ( LINES OF lt_spfli_1 )
                         ( LINES OF lt_spfli_2 )
                         ( carrid = 'AV' ) ) ASSIGNING FIELD-SYMBOL(<spfli>).
 WRITE <spfli>-carrid.
ENDLOOP.

Got this snippet from George Drakos talk from the ABAPConf 2024. Check his Github.

[ABAP] CATS – Difference between two times (CATSHOURS)

I had to calculate the difference between two times, even if the end time goes beyond the 0 o’clock day limit, e.g. 20:00 to 02:00 should be 6h. I found the following two function modules doing the job:

* Option 1
CALL FUNCTION 'CATS_COMPUTE_HOURS'
  EXPORTING
    pernr                    = pernr
    date                     = date
*   NO_BREAK_DEDUCTION       = ' '
    row                      = row
  TABLES
    return                   = return
  CHANGING
    catshours                = catshours
    beguz                    = beguz
    enduz                    = enduz
    vtken                    = vtken.

* Option 2 (I think it's only available on S/4 HANA)
CALL FUNCTION 'CATS_DETERMINE_HOURS'
  CHANGING
    catshours                = catshours
    beguz                    = beguz
    enduz                    = enduz.

[HR] Anzeige Zeitauswertungsergebnisse (Cluster B2) – Zeitsalden kumuliert (SALDO)

PT_CLSTB2 → SALDO

Alternative Report: RPCLSTB2 – Anzeige Zeitauswertungsergebnisse (Cluster B2)

DATA pernr TYPE pernr_d.
DATA saldo TYPE TABLE OF pc2b5.

CALL FUNCTION 'HR_TIME_RESULTS_GET'
  EXPORTING
    get_pernr             = pernr
    get_pabrj             = CONV pabrj( sy-datum(4) )
    get_pabrp             = CONV pabrp( sy-datum(6) )
  TABLES
    get_saldo             = saldo
  EXCEPTIONS
    no_period_specified   = 1
    wrong_cluster_version = 2
    no_read_authority     = 3
    cluster_archived      = 4
    technical_error       = 5
    OTHERS                = 6.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.

[ABAP] Import Transport from ZIP

Report to import a ZIP file, containing the cofiles and data parts of a transport request. Related reports:
https://nocin.eu/abap-download-transport-as-zip/
https://nocin.eu/abap-create-toc-for-a-given-transport-release-it-and-download-it-as-zip/

This report can be handy, especially since S/4HANA 2023 seems to have restricted the “classic” import way by using TCode CG3Y and CG3Z. See note 1949906, where it is recommended to create a custom report.

*&---------------------------------------------------------------------*
*& Report Z_IMPORT_TRANSPORT_FROM_ZIP
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT z_import_transport_from_zip.

SELECTION-SCREEN BEGIN OF BLOCK bl02 WITH FRAME TITLE TEXT-t02.
  PARAMETERS p_lcldir TYPE string  LOWER CASE OBLIGATORY DEFAULT 'C:\temp\'.
  PARAMETERS p_sapdir TYPE char255 LOWER CASE OBLIGATORY.
SELECTION-SCREEN END OF BLOCK bl02.

SELECTION-SCREEN BEGIN OF BLOCK bl03 WITH FRAME.
  PARAMETERS p_import TYPE boolean DEFAULT 'X' AS CHECKBOX.
SELECTION-SCREEN END OF BLOCK bl03.


AT SELECTION-SCREEN OUTPUT.

  CALL 'C_SAPGPARAM' ID 'NAME' FIELD 'DIR_TRANS' ID 'VALUE' FIELD p_sapdir.


AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_lcldir.

  DATA lt_files  TYPE filetable.
  DATA lv_rc     TYPE i.
  DATA lv_action TYPE i.

  TRY.
      cl_gui_frontend_services=>file_open_dialog( EXPORTING window_title      = 'Import'
                                                            default_extension = '.zip'
                                                            initial_directory = 'C:\temp\'
                                                            multiselection    = abap_false
                                                  CHANGING  file_table        = lt_files
                                                            rc                = lv_rc
                                                            user_action       = lv_action ).
      IF lv_action = cl_gui_frontend_services=>action_ok AND lines( lt_files ) > 0.
        p_lcldir = lt_files[ 1 ]-filename.
      ENDIF.
    CATCH cx_root INTO DATA(e_text).
      MESSAGE e_text->get_text( ) TYPE 'I'.
  ENDTRY.


START-OF-SELECTION.

  DATA lv_zip_size TYPE i.
  DATA lt_zip_data TYPE solix_tab.
  DATA lv_xstring  TYPE xstring.

  cl_gui_frontend_services=>gui_upload( EXPORTING filename   = p_lcldir
                                                  filetype   = 'BIN'
                                        IMPORTING filelength = lv_zip_size
                                        CHANGING  data_tab   = lt_zip_data ).

  CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
    EXPORTING
      input_length = lv_zip_size
    IMPORTING
      buffer       = lv_xstring
    TABLES
      binary_tab   = lt_zip_data.


  " write zip content to filesystem
  DATA(lo_zipper)      = NEW cl_abap_zip( ).
  DATA(lt_zip_entries) = lo_zipper->splice( lv_xstring ).
  lo_zipper->load( lv_xstring ).

  LOOP AT lt_zip_entries INTO DATA(ls_zip_entry).

    lo_zipper->get( EXPORTING name    = ls_zip_entry-name
                    IMPORTING content = DATA(lv_data) ).

    DATA(lv_file) = COND #( WHEN ls_zip_entry-name(1) = 'K' THEN p_sapdir && '/cofiles/' && ls_zip_entry-name
                                                            ELSE p_sapdir && '/data/'    && ls_zip_entry-name ).

    TRY.
        OPEN DATASET  lv_file FOR OUTPUT IN BINARY MODE.
        TRANSFER      lv_data TO lv_file.
        CLOSE DATASET lv_file.
      CATCH cx_root INTO DATA(e_text).
        MESSAGE e_text->get_text( ) TYPE 'E'.
    ENDTRY.

  ENDLOOP.


  " now add transport to stms
  DATA lv_ret_code    TYPE trretcode.
  DATA ls_exception   TYPE stmscalert.
  DATA lt_logptr      TYPE TABLE OF tplogptr.
  DATA lt_stdout      TYPE TABLE OF tpstdout.

  DATA(lv_trkorr) = CONV trkorr( lt_zip_entries[ 1 ]-name+8(3) && 'K' && lt_zip_entries[ 1 ]-name+1(6) ).
  DATA(lv_system) = CONV tmssysnam( sy-sysid ).
  SELECT SINGLE domnam FROM tmscsys INTO @DATA(lv_transport_domain).

  " add transport to queue
  CALL FUNCTION 'TMS_MGR_FORWARD_TR_REQUEST'
    EXPORTING
      iv_request     = lv_trkorr
      iv_tarcli      = sy-mandt
      iv_target      = lv_system
      iv_source      = lv_system
      iv_tardom      = lv_transport_domain
      iv_srcdom      = lv_transport_domain
    IMPORTING
      ev_tp_ret_code = lv_ret_code
      es_exception   = ls_exception
    TABLES
      tt_stdout      = lt_stdout
    EXCEPTIONS
      OTHERS         = 99.
  IF sy-subrc <> 0 OR lv_ret_code <> 0.
    cl_demo_output=>display( lt_stdout ).
    MESSAGE 'Could not add transport to queue' TYPE 'E'.
  ENDIF.

  " also directly import if checked
  IF p_import = abap_true.
    CALL FUNCTION 'TMS_MGR_IMPORT_TR_REQUEST'
      EXPORTING
        iv_system                  = lv_system
        iv_request                 = lv_trkorr
        iv_client                  = sy-mandt
        iv_ignore_cvers            = abap_true "ignore invalid component version vector (CVERS)
      IMPORTING
        ev_tp_ret_code             = lv_ret_code
        es_exception               = ls_exception
      TABLES
        tt_logptr                  = lt_logptr
        tt_stdout                  = lt_stdout
      EXCEPTIONS
        read_config_failed         = 1
        table_of_requests_is_empty = 2
        OTHERS                     = 3.
    IF sy-subrc <> 0 OR lv_ret_code <> 0.
      cl_demo_output=>display( lt_stdout ).
      MESSAGE 'Could not import transport' TYPE 'E'.
    ENDIF.
  ENDIF.

  MESSAGE 'Transport successfully imported' TYPE 'S'.

[Workflow] Get all Workitems and Workitem Container related to a pernr

If you have connected any workflows to a pernr via BUS1065 (like it is described here), you can receive all related workflows/workitems related to this pernr via the following code:

    NEW cl_def_im_com_bsp_workflow( )->if_ex_com_bsp_workflow~read_workitems_for_object( EXPORTING iv_swo_objtype = 'BUS1065'
                                                                                                   iv_swo_objkey  = CONV #( lv_pernr ) 
                                                                                         IMPORTING et_workitems   = DATA(lt_workitems) ).

    LOOP AT lt_workitems ASSIGNING FIELD-SYMBOL(<workitems>).
      "If you are only interested in specific workflows, you could filter here
      "WHERE wi_rh_task = 'WSxxxxxxxx' 
      "AND   wi_stat    =  'STARTED'.

      NEW /iwwrk/cl_wf_read_workitem( <workitems>-wi_id )->get_wi_container( IMPORTING et_wi_container = DATA(lt_wi_container) ).
      " access container items via lt_wi_container[ element = 'IV_PERNR'  ]-value

    ENDLOOP.

[ABAP] Validate JSON data

While searching on how to validate a given JSON string, I found two options. The first simply returns a Boolean value, the second also returns information about what could be wrong.

DATA(lv_json) = '{'
             &&      '"employee": {'
             &&           |"name" : "Max", |
             &&           |"age"  : 43,    |
             &&      '}'
             && '}'.


" option 1:
DATA(is_valid) = /ui5/cl_json_util=>is_wellformed( lv_json ).

" option 2: 
DATA(lo_reader) = cl_sxml_string_reader=>create( cl_abap_codepage=>convert_to( lv_json ) ).
TRY.
    lo_reader->next_node( ).
    lo_reader->skip_node( ).
  CATCH cx_sxml_parse_error INTO DATA(lx_parse_error).
    WRITE lx_parse_error->get_text( ).
ENDTRY.

[ABAP] Generate a QR-Code

There are many blogs describing how to create a QR-Code in the context of SAPscript or Smartforms (e.g. here, here, here and here). But I was looking for a way to generate a QR-Code and only receive the graphical data stream from it, without the need for any manual steps such a creation via SE73. During my search, I found these two notes, which contained all the information I needed:

  • 2790500: mentions the class CL_RSTX_BARCODE_RENDERER with method QR_CODE
  • 2030263: describes the parameters for a QC-Code creation

Following my little test report:

PARAMETERS p_text TYPE string DEFAULT 'My QR-Code Content'.

DATA: lv_action   TYPE i.
DATA: lv_filename TYPE string.
DATA: lv_fullpath TYPE string.
DATA: lv_path     TYPE string.

TRY.
    cl_rstx_barcode_renderer=>qr_code( EXPORTING i_module_size      = 25                " Size of smallest module (in pixel, max: 32000)
                                                 i_barcode_text     = p_text            " Barcode text
*                                                 i_mode             = 'A'               " Mode ('N', 'A', 'L', 'B', 'K', 'U', '1', '2'; note 2030263)
*                                                 i_error_correction = 'H'               " Error correction ('L', 'M', 'Q', 'H')
*                                                 i_rotation         = 0                 " Rotation (0, 90, 180 or 270)
                                       IMPORTING e_bitmap           = DATA(e_bitmap) ). " Bitmap in BMP format

    DATA(lt_raw_data) = cl_bcs_convert=>xstring_to_solix( e_bitmap ).

    " Save-Dialog
    cl_gui_frontend_services=>file_save_dialog( EXPORTING default_file_name = 'QR-Code'
                                                          default_extension = 'bmp'
                                                          file_filter       = |{ cl_gui_frontend_services=>filetype_all }|
                                                CHANGING  filename          = lv_filename
                                                          path              = lv_path
                                                          fullpath          = lv_fullpath
                                                          user_action       = lv_action ).

    IF lv_action EQ cl_gui_frontend_services=>action_ok.

      " Download file to disk
      cl_gui_frontend_services=>gui_download( EXPORTING filename     = lv_fullpath
                                                        filetype     = 'BIN'
                                                        bin_filesize = xstrlen( e_bitmap )
                                              CHANGING  data_tab     = lt_raw_data ).
    ENDIF.

  CATCH cx_rstx_barcode_renderer INTO DATA(lo_exp).
    MESSAGE lo_exp->get_text( ) TYPE 'I'.
ENDTRY.