nocin.eu

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

[Linux Mint] Start Applications minimized (start in tray)

The fact that some applications do not start minimized (in tray) at system startup has been annoying me for quite some time. I find it even more annoying that you can’t simply set this directly via a checkbox in the Startup Applications for each application. The problem seems to be that each application has a different parameter for this, and therefore it cannot be done generally by the operating system (at least that’s my guess). I have therefore researched the necessary parameters for the applications I use. Simply add the parameter at the end of the Startup Applications command. For some applications, you can also activate it directly in the specific settings.

ApplicationSetting / Command
Bitwarden (Flatpack)File ⇾ Settings ⇾ App Settings ⇾ Start to tray icon
Netxcloud–background
SignalFile ⇾ Preferences ⇾ General ⇾ System ⇾ Start minimized to system tray
Steam-silent
Syncthing GTK–background
Telegram-autostart -startintray
Transmission remote GUI-hidden

Helpful discussion: https://askubuntu.com/questions/663187/how-can-i-run-a-program-on-startup-minimized

[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] My Overtime Requests

Here are the notes I took while investigating SAP’s relatively ‘new’ overtime apps.

SAP Fiori for SAP Business Suite
My Overtime Requests (Fiori 2.0)
Approve Overtime Requests (Fiori 2.0)

SAP S/4HANA
My Overtime Requests (Fiori 2.0)
Approve Overtime Requests (Fiori 2.0)

SAP Fiori Apps Reference Library
F4939
F4937

Component:
PA_FIO_OVT

Packages:
– PAOC_TIM_OVERTIMEQTA_REQ_CORE (Mein Mehrarbeitskontingent: Core-Funktionen)
– PAOC_TIM_OVERTIMEQTA_REQ_UIA (UIA = User Interface Adapter)
– PAOC_TM_TIMEDATE_REQ_CORE (Zeitdatenanforderung: Core-Funktionen)
– ODATA_HCMFAB_MYOVERTIMEQUOTA
– HCM_FAB_MYOVERTIMEQUOTA (contains BSP HCMFAB_OT_MAN)
– HCM_FAB_APPROVE_OVERTIME (contains BSP HCMFAB_OTQ_APR)

Reports

It is recommended to run the reports RPT_OVT_POST and RPT_OVT_EMAIL as a job.

Using RPT_OVT_UIA_TEST, you can simply simulate Overtime Requests in the backend to test your customizing.

OData
– HCMFAB_MYOVERTIMEQUOTA_SRV (service name)
– HCMFAB_MYOVERTIMEQUOTA (SEGW Project)

Workflow
– WS02400057 (PT_OVTREQ)

Cluster Tables
– PTREQ_HEADER
– PTREQ_ITEMS
– PTREQ_ACTOR
– PTREQ_NOTICE

On Behalf of” is supported
2806560

Customizing
SPRO → Personnel Management → Employee Self-Service (Web Dynpro ABAP)  → Service-Specific Settings →  Working Time → My Overtime Requests.

Adjusting the Fiori UI

If you want to hide fields in the Fiori frontend, or you want to adjust some labels, you can simply do this via Customizing:
My Overtime Requests → Layout of the Web Application → Define Field Selection

Multi-Level Approval for Time Data Requests (HRPT_B_TMD_MULTI_APPROVERS)

The BAdI is called when opening the My Overtime Request App and a second time when clicking on the New Button to create a new OVT request. It’s not triggered when sending the request, what I initially expected.
To reuse the code from the sample class,

*    "Determine approver level
*    DATA(cust_key_request_fields) = VALUE cl_hrpt_ovt_const=>ovt_cust_key_request_fields( pernr = iv_pernr
*                                                                                          ktart = iv_subtype
*                                                                                          date  = is_request_period-start_date ).
*    TRY.
*        DATA(approval_settings) = cl_hrpt_ovt_cust_factory=>get_customizing_reader( )->get_approval_settings( REF #( cust_key_request_fields ) ).
*      CATCH cx_hrpt_tmd.
*        RETURN.
*    ENDTRY.
*    IF approval_settings-use_multiple_approver = abap_false.
*      RETURN.
*    ENDIF.

....

*    "If no last approvers exist, use line manager, HR BP and HR Admin by default
*    CALL FUNCTION 'BAPI_GET_LINE_MANAGER'
*      EXPORTING
*        im_objid       = iv_pernr
*      IMPORTING
*        es_approver    = line_manager
*        ev_has_manager = approver_exists.
*    IF approver_exists = abap_true.
*      last_seqnr = last_seqnr + 1.
*      line_manager-seqnr = last_seqnr.
*      APPEND line_manager TO rt_approver_tab.
*      IF lines( rt_approver_tab ) >= approval_settings-approver_level.
*        RETURN.
*      ENDIF.
*    ENDIF.

you have to set the amount of approval steps in the customizing.

Custom Workflow

If you want to replace the default Workflow WS02400057 (PT_OVTREQ) with a custom one, you must adjust the customizing (as described here for the standard WF), and you’ll need to create a new BAdI Implementation for HCMFAB_MYOTQ_APPROVAL_INBOX analog HCMFAB_MYOTQ_APPROVAL_INBOX to get the Approve and Rejects Buttons working by providing the Workflow-ID and decision Step-ID.

Extensibility

Although it should be possible to extend both Fiori Apps via Adaptation Projects, I was unable to do so. Either it’s a problem with the new adaption editor, which got new released while writing the blog, or it is a problem with the apps itself. Whatever I tried, I was always running in issues.

Therefore, I did it the “classic” way and created an Extension Project and used the provided Extension Points. For the Approval App, I was unable to preview the application directly in BAS, perhaps because it is only an Inbox integration and does not run standalone. I therefore always had to deploy, to test my changes.

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

PT_CLSTB2 → SALDO

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.

[Fiori] BAS – Request is not a local Request

Recently I was continuing an enhancement project of a standard Fiori Application. The already existing enhancement project was created using WebIDE a few years ago. Now I needed to add some more functionality to the enhancement project using the Business Application Studio. To my surprise, the automatic migration of the WebIDE project to a BAS project was working without any issues. Nice! I could make my changes and everything was fine until I tried to deploy the application.

The abap_deploy_task aborted with the error “Request is not a local Request” (in German “Auftrag ist kein lokaler Auftrag“).

Somehow it did not deploy in my Workbench Transport Request and was expecting a Local Transport Request.

At first, I thought, this was a problem in the ui5-deploy.yaml configuration of my App, and therefore I was looking in the wrong direction. But after checking the package of the already deployed enhancement project in the backend, I finally found the reason. The Transport Layer of the Package containing the BSP application was not correct. It turned out, that the previous developer was not able to deploy from his WebIDE directly to the system, instead he deployed to another system, exported and then imported the transport request manually. Therefore, the “Original System” of all objects was incorrect and also the Transport Layer on the Package. After updating both (like described here), I was able to deploy the enhancement project successfully in my Workbench Transport Request.

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

[Fiori] Launchpad: Failed to load catalogs

Recently, I was confronted with the “Failed to load catalogs” error message when starting the Fiori Launchpad (/ui2/flp) and navigating to the App Finder.

The system was quite new and the Fiori Customizing was not completely done yet. When opening the Dev Tools, I saw a failed request to

/sap/opu/odata/ui2/page_builder_pers/PageSets('%2FUI2%2FFiori2LaunchpadHome')

Opening this failed request in a new tab results in an HTTP 404 from an Apache web service. So it looked like an Apache was set up as reverse proxy in front of the sap system. I tried to call the PageSets endpoint without providing the key %2FUI2%2FFiori2LaunchpadHome and got an HTTP 200. So in general, Apache was working, and the service endpoint page_builder_pers was responding. Next, I did the same service calls from the gateway client and both calls were working fine. So it looked like Apache was the problem when providing the PageSets key. After a quick search, I found this post on Stack Overflow:

https://stackoverflow.com/questions/37834925/apache-reverse-proxy-blocking-sap-fiori-launchpad-url

So the issue was some (in this case) incorrect decoding of ‘%2F’. After contacting the basis team and adding the proposed Apache configs, the request finally resolved successfully, and the Launchpad was displaying some apps.