* normal way
DATA(rs1) = VALUE tt_rsdsselopt( ( sign = 'I' option = 'EQ' low = 'one' )
( sign = 'I' option = 'EQ' low = 'two' )
( sign = 'I' option = 'EQ' low = 'three' ).
* short way using factorization
DATA(rs1) = VALUE tt_rsdsselopt( sign = 'I' option = 'EQ' ( low = 'one' )
( low = 'two' )
( low = 'three' ) ).
Category: ABAP
ABAP
[Workflow] How do delete a Workflow in status COMPLETED
While searching for a way to delete a workflow, I came across this blog post: https://community.sap.com/t5/technology-blogs-by-members/how-to-logically-delete-workflows/ba-p/12991725
Unfortunately, logically deleting workflows is only possible when the workflow is not in the status COMPLETED.

Since my workflow was already in this state, I had to find another way, and found it with transaction code SWWL. Simply find the unique Identification of the top level workitem via t-code SWIA and then use it in SWWL. When running the report, you will first get a list, then simply select the result items you want to delete and hit the trash icon, or restart the selection and check the flag for Delete immediately.

[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

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.
Um direkt für mehrere Perioden zulesen, diesen Baustein verwenden:
DATA: time_results TYPE TABLE OF ptm_time_results.
CALL FUNCTION 'HR_TIME_RESULTS_IN_INTERVAL'
EXPORTING
int_pernr = pernr-pernr
int_begda = pn-begda
int_endda = pn-endda
TABLES
int_time_results = time_results
EXCEPTIONS
no_period_specified = 1
wrong_cluster_version = 2
no_read_authority = 3
cluster_archived = 4
technical_error = 5
OTHERS = 6.
[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] Read work schedule
DATA(work_schedule) = cl_hcmfab_employee_api=>get_instance( )->get_work_schedule( iv_pernr = 1
iv_begda = sy-datum
iv_endda = sy-datum ).
cl_demo_output=>display_data( work_schedule ).
[Workflow] Display all Workflows related to an Employee
PA20 – Workflow-Overview


To attach your own workflow in this overview, you have to add the Task TS51900010 to your workflow and pass over the employee number to the Business Object “Employee” (BUS1065)


[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.
