Skip to Content
Technical Articles
Author's profile photo Bärbel Winkler

How to trigger multiple ATC-checks in one go

Encouraged by the feedback I received in the Coffee Corner, here is a 2nd Q&A turned into a blog post. I decided to do this in this particular case because I didn’t just get a very simple solution to my question but because it also examplifies how helpful the SAP community can be – and even in possibly record time!

About a year ago and in preparation for our upgrade to the SAP HANA database I started to look into the SCI/ATC options and how to make good use of them. One thing which was bugging me was that I wasn’t able to find anything about how to call the ATC from a report program and provide the results in one go for multiple objects/transports. So, I posted a question about just that in Answers on Feb. 22, 2017 at 8:06 in the morning my time In Germany. Imagine my surprise, when I got a notification about a response from Axel Jebens  within an hour – at 8:48 to be precise! – which was short and to the point:

Hi,

There is an object-oriented API which you can use to create an own program based on class CL_SATC_API_FACTORY. Please check out the example program RSATC_API_USAGE_EXAMPLE. There is a remote-enabled API and example program, too. See program RSATC_API_REMOTE_USAGE_EXAMPLE.

Hope it helps!

This turned out to be exactly what I was looking for and it was easy to implement as well! I checked out the sample code, copied it into a test-program, tweaked some of the hard-coded values to fit our own data (i.e. the program name or transport ID) and checked the results of this prototyping.

The next step was to add the logic – with some more tweaks – into a program I already had which lets me select transport-requests by various criteria. I basically just needed to add another routine to call the API from within a loop through the table with the already selected transports. I also decided to ignore any errors along the way (apart from a wrong check-variant) as I’m mainly using this for spot checks. I already had the program running each morning to send an email with a list of currently open transport-requests so I added the ATC-summary to that email. The ATC-information just includes the transport-ID and the number of findings encountered, but the details can be looked at as needed via Tcode ATC in the dev-system. So, as time allows, I can now use that email to do some spot checks on e.g. transports showing many findings. I had this up and running around noon.

Here is how the tweaked code looks like:

METHOD trigger_atc.

  "Local definitions for ATC API calls
  DATA:
    l_factory            TYPE REF TO cl_satc_api_factory,
    l_object_set         TYPE REF TO if_satc_object_set,
    l_variant            TYPE REF TO if_satc_check_variant,
    l_configuration      TYPE REF TO if_satc_run_configuration,
    l_controller         TYPE REF TO if_satc_run_controller,
    l_result_access      TYPE REF TO if_satc_result_access,
    l_findings           TYPE scit_rest,
    l_findings_extension TYPE satc_ci_findings_extension,
    l_ext_field_list     TYPE satc_ci_finding_ext_field_list,
    l_msg                TYPE string,
    l_cx                 TYPE REF TO cx_root,
    l_description(128)   TYPE c.

  "Table for transport numbers
  DATA: BEGIN OF ls_transp,
          strkorr LIKE e070ctv-strkorr,
          as4user     LIKE e070ctv-as4user,
          name_last   LIKE user_addr-name_last,
        END   OF ls_transp.

  DATA: lt_transp LIKE TABLE OF ls_transp.

  DATA: ls_dl_objects LIKE LINE OF gt_dl_objects.

  "Restrict to relevant ABAP-objects (via sel-screen input)
  LOOP AT gt_dl_objects INTO ls_dl_objects WHERE pgmid  IN s_pgmid
                                             AND object IN s_obj_em
                                             AND trkorr IN s_cts.
    "Add transport-ID to internal table for summary listing in email
    ls_transp-strkorr = ls_dl_objects-strkorr.
    ls_transp-as4user = ls_dl_objects-as4user.
    ls_transp-name_last = ls_dl_objects-name_last.
    COLLECT ls_transp INTO lt_transp.
  ENDLOOP.

  LOOP AT lt_transp INTO ls_transp.

    CREATE OBJECT l_factory.
    TRY.
        l_object_set = cl_satc_object_set_factory=>create_for_transport( i_transport = ls_transp-strkorr ).
      CATCH cx_satc_empty_object_set INTO l_cx.
        IF sy-batch EQ gc_x.
          MESSAGE i000(38) WITH 'Processing of transport'(E03) ls_transp-strkorr 'ended with error:'(e04).
          MESSAGE l_cx TYPE 'I'. " Object set contains no checkable objects
        ENDIF.
        CONTINUE.
      CATCH cx_satc_not_found INTO l_cx.
        IF sy-batch EQ gc_x.
          MESSAGE i000(38) WITH 'Processing of transport'(E03) ls_transp-strkorr 'ended with error:'(e04).
          MESSAGE l_cx TYPE 'I'. " Object set contains no checkable objects
        ENDIF.
        CONTINUE.
      CATCH cx_satc_invalid_argument INTO l_cx.
        IF sy-batch EQ gc_x.
          MESSAGE i000(38) WITH 'Processing of transport'(E03) ls_transp-strkorr 'ended with error:'(e04).
          MESSAGE l_cx TYPE 'I'. " Object set contains no checkable objects
        ENDIF.
        CONTINUE.
      CATCH cx_satc_failure INTO l_cx.
        IF sy-batch EQ gc_x.
          MESSAGE i000(38) WITH 'Processing of transport'(E03) ls_transp-strkorr 'ended with error:'(e04).
          MESSAGE l_cx TYPE 'I'. " Object set contains no checkable objects
        ENDIF.
        CONTINUE.
    ENDTRY.

    TRY.
        l_variant = l_factory->get_repository( )->load_ci_check_variant( i_name = p_chkv ).
      CATCH cx_satc_not_found INTO l_cx.
        MESSAGE l_cx TYPE 'E'. " Specified Code Inspector variant was not found
    ENDTRY.

    CONCATENATE p_chkv
                ls_transp-strkorr
                sy-datum
                sy-uzeit
                ls_transp-as4user
                ls_transp-name_last
           INTO l_description
      SEPARATED BY ' - '.


    l_configuration = l_factory->create_run_config_with_chk_var(
      i_object_set    = l_object_set
      i_check_variant = l_variant
      i_description   = l_description ).

    l_configuration->set_pragma_option( i_option = if_satc_ac_project_constants=>c_mode_fndng_xmptd_in_code-show_as_open ).

    l_controller = l_factory->create_run_controller( l_configuration ).

    TRY.
        l_controller->run( IMPORTING e_result_access = l_result_access ).
      CATCH cx_satc_failure INTO l_cx.
        MESSAGE l_cx TYPE 'E'. " ATC check run failed (no authorization, etc.)
    ENDTRY.

    l_ext_field_list-package_name = abap_true.
    l_ext_field_list-exc_validity = abap_true.

    TRY.
        l_result_access->get_findings(
          EXPORTING
            i_ext_field_list     = l_ext_field_list
          IMPORTING
            e_findings           = l_findings
            e_findings_extension = l_findings_extension ).
      CATCH cx_satc_failure INTO l_cx.
        MESSAGE l_cx TYPE 'E'. " Result access failed (no authorization, etc.)
    ENDTRY.

    l_msg = 'Number of Findings / Extensions: &1 / &2'(I01).
    REPLACE ALL OCCURRENCES OF '&1' IN l_msg WITH |{ lines( l_findings ) }|.
    REPLACE ALL OCCURRENCES OF '&2' IN l_msg WITH |{ lines( l_findings_extension ) }|.

    CONCATENATE l_msg
                ls_transp-strkorr
                ls_transp-as4user
                ls_transp-name_last
           INTO l_msg
      SEPARATED BY space.

    gs_atc_msg-msg = l_msg.
    APPEND gs_atc_msg TO gt_atc_msg.

  ENDLOOP.


  "Add the ATC-summary to the simple list
  SKIP 1.
  WRITE: / 'Summary of ATC-findings:'(i02).

  LOOP AT gt_atc_msg INTO gs_atc_msg.
    WRITE: / gs_atc_msg-msg.
  ENDLOOP.

ENDMETHOD.

So, from asking the question to having a working solution implemented took not even 5 hours in this case. Which is quite a quick turnaround as far as I can tell!

Do you have similar examples of where the community came through this quickly?

Assigned Tags

      8 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      I'm pretty sure I have a similar example of fast "customer service" on SCN 🙂 but it's somewhere in the archives under the previous profile. Most of my new "questions" these days are the complaints about the website. 🙂

      Thank you for posting this! We're just starting to look at better ways to organize our work and I'm pretty sure ATC check will be a part of it. So this will be very useful.

      Author's profile photo Bärbel Winkler
      Bärbel Winkler
      Blog Post Author

      Here is an example where being able to execute ATC-checks "in bulk" came in very handy during our project to do ATC-checks via a central system and moving towards blocking on transport release:

      Once I had created the baseline in the ATC-system and set up a central check variant, I defined another daily job for my transport-selection program, looking at currently still modifiably transports and triggering the new central check for them. I then checked the ATC-results on a daily basis and used the information to tweak things like the message priorities for the central check variants as needed. The daily results showed clear progress with more and more findings ending up with priority 3 instead of 1 or 2 as intended:

      This example is also described in Setting up a central ATC-system – Part 3: Tweaking the settings to our liking.

      Cheers

      Baerbel

       

       

      Author's profile photo Pablo Costantini
      Pablo Costantini

      Bärbel Winkler Do you have this in some public repository?

      Author's profile photo Bärbel Winkler
      Bärbel Winkler
      Blog Post Author

      Pablo Costantini

      Hi Pablo,

      do you mean the code to trigger the ATC-checks or something else? If the former, all I can offer is the relevant code snippet explained in this blog post about triggering ATC-checks. The "surrounding code" in the program I have this in is not in any shape or form to be shared as I put it together "haphazardly" over the years, so it's not even "tidy code" let alone clean code!

      Cheers

      Bärbel

      Author's profile photo Asanka Liyanage
      Asanka Liyanage

      Hi Bärbel,

      Thank you for your valuable blog, which helped us to understand the flow of the ATC process when we tried to display findings programmatically. we have used these ATC classes to get the details of findings. our requirement is to customise the output to show summarised/unified results when we run ATC checks for multiple transports.

      could you please let me know where should I look at to find the structure for the display results. we are not using fm 'SATC_AC_DISPL_RESULT_BY_EXEC'. we have almost similar format though.

       

      Thank you!

      Asanka.

      Author's profile photo Bärbel Winkler
      Bärbel Winkler
      Blog Post Author

      Hi Asanka,

      I'm sorry but I won't be able to answer your question as I won't be back in the office with access to our system until mid-October. And even then, I'm not sure if I can provide more information than what is shown in the blog post.

      Cheers

      Bärbel

      Author's profile photo Asanka Liyanage
      Asanka Liyanage

      Thanks Bärbel. I understand. Sometimes there are limitations.

      Cheers,

      Thanks for the blog again.

      Asanka

      Author's profile photo Dario Fraccon
      Dario Fraccon

      If you want to perform checks for specific objects or requests for transport requests you can also use these functions.

      DATA lv_CURRENT TYPE slin_bool.
      DATA lV_ERROR_TYPE TYPE trwbo_charflag.
      
      CALL FUNCTION 'TRINT_INSPECT_OBJECTS'
      EXPORTING
      iv_mode = iv_mode
      it_e071 = lt_e071_objects
      IMPORTING
      ev_current = lv_current
      ev_error_type = lv_error_type
      EXCEPTIONS
      canceled_by_user = 1
      OTHERS = 2.
      
      IF sy-subrc = 0.
      CALL FUNCTION 'TR_GET_OBJ_INSPECTION_RESULTS'
      IMPORTING
      et_summary = et_check_summary
      eo_slin_res = eo_slin_res
      ev_atc_res = ev_atc_res
      et_docu_res = et_docu_res
      et_pack_res = et_pack_res
      et_gtabkey_res = et_gtabkey_res
      eo_atc_res = eo_atc_res.
      ENDIF.
      
      IF ev_atc_res IS NOT INITIAL.
      
       DATA lt_findings_det TYPE STANDARD TABLE OF scir_rest WITH NON-UNIQUE SORTED KEY k1 COMPONENTS code test cHECKSUM1.
      
       DATA(result_access) = NEW cl_satc_api_factory( )->create_result_access( CONV satc_d_result_id( ev_atc_res ) ).
      
       result_access->get_findings( IMPORTING e_findings = DATA(lt_findings_msg) ).
       INSERT LINES OF lt_findings_msg INTO TABLE lt_findings_det.
      *FREE lt_findings_msg.

      However, in my case I also needed to find the priority of the error, and I solved this by directly accessing the ATC tables (in this case cds)

      SELECT dv~* FROM satc_Ac_Rstit_Ddlv AS dv
      INTO TABLE @DATA(db_findings)
      WHERE dv~display_id EQ @ev_atc_res
      AND dv~exc_validity NE 'E'
      AND dv~priority IN @s_prpty
      AND dv~status_new < @if_satc_ac_result_db_model=>co_status-completed.
      IF sy-subrc NE 0.
      MESSAGE i151.
      STOP.
      ENDIF.