Skip to Content

As an SAP customer, we have lots of small development topics (change requests) every year. Most of the change requests are tested and then transported into the production system within a short time frame. However, there are always some change requests which are abandoned for various reasons.  Periodically, my colleagues in the Basis (administration) Team create a list of these “long running changes”, and ask the developers to either release them or rolling them back (revert the objects to the original state, as in the productive system. This is unpleasant work for both the Basis colleagues and the developers.

To avoid this, we implemented a little report that automatically reverts the development and customizing objects in selected open transports to the state that they have in the production system.

It does this by creating a “mirror transport” in the production system.

Two pieces of coding are needed:

  • An RFC function module in the production system, which is called with the object list, and adds them to the open transport of copies
  • A small report for the development system, which performs some checks, creates backup versions of the objects, and then calls the function module

I attach the coding in “new” 7.40 SP08 syntax as text files. Of course, it can be rewritten to work on older ABAP versions as well. The function module logic is implemented in a local class, so the text file consists of 3 parts. In addition to the code, one message has to be created for the function module – see comment in code. There are 480 lines of code in total.

Besides executing the report, 4 manual steps are necessary. The sequence is:

  1. Manually: Create an empty “transport of copies” in the production system (P system), to use as a mirror transport. Target = development system + client (if you do not enter a target system, no transport file is created!)
  2. Using the new report (executed in the development system), fill the “mirror transport” in the P system automatically, based on a list of open transports (from the selection screen). The report also deletes empty transport tasks and requests, releases all transport tasks that are not yet released, checks  if any of the objects in the transports have inactive versions (because these would not be overwritten in step 4), and creates versions of all objects
  3. Manually: Release the mirror transport in the P system. Ignore the warning message “Not all objects could be identified due to missing object catalog entry”.
  4. Manually: Import the mirror transport into the Development system. You have to choose the “Overwrite originals” option (and if there are any modifications involved, “overwrite objects in repairs”).
  5. Manually: Release and transport the original transport requests

Known limitation: The report does not work for customizing if source and target client differ.

Disclaimer: I have tested the process and code in our own systems, but of course I cannot guarantee that it is bug-free, works as specified, etc.

Also, I do not have experience how this might work in complex landscapes with several development systems. (My first assumption is, that it does not introduce any additional risks or complexity.)

 

Update history:

29.06.2016: language edit: decommission –> roll back

Warning concerning “overwrite objects in repairs”, later removed because of updated code

30.06.2016: sentence about complex landscapes in disclaimer

04.07.2016: Code updated with check for inactive objects

07.07.2016: Code updated according to Daniel Klein’s feedback. Limitation regarding customizing / different clients mentioned.

21.07.2016: Code updated (V4): automatically delete empty tasks and requests

16.11.2016: Code added to blog directly, because attachments are not possible with new SCN. Small bugfix (search for “SCN Version 4” in the code). Added “known problem” to code as comment.

*----------------------------------------------------------------------*
* Author: Edo von Glan
* Version 5 (16.11.2016)
*----------------------------------------------------------------------*
* Known problem:
*   - if the transport of copies contains table (TABL) and related table entries (TABU),
*     AND the table does not exist in the productive system,
*     then we get strange errors when releasing (unknown syntax, table does not exist, ...)
*     when we try to release the transport of copies in the P system.
*     Solution: manually delete the table contents entry.
*     (In the program, we could avoid this situation by
*     checking in the RFC in the P system for all TABU records, whether
*     the table exists)
*----------------------------------------------------------------------*
REPORT zs_decom_obsol_trans.

TABLES: e070.

SELECT-OPTIONS s_trans FOR e070-trkorr.                       " Trans.Requests 2 B rolled back
PARAMETERS: p_p_tr TYPE trkorr OBLIGATORY                     " Transprt of copies on P System
          , p_p_sys TYPE rfcdest OBLIGATORY DEFAULT 'MT3_050' " Productive system (RFC-Dest.)
          .

CLASS lcl DEFINITION FINAL.
  PUBLIC SECTION.
    CLASS-METHODS main.
  PRIVATE SECTION.
    CLASS-METHODS:
      process_trkorr
        IMPORTING i_trkorr TYPE trkorr,
      prepare_trkorr_ok
        IMPORTING i_trkorr       TYPE trkorr
        RETURNING VALUE(r_is_ok) TYPE abap_bool,
      prevent_inactive_objects_ok
        IMPORTING i_trkorr       TYPE trkorr
        RETURNING VALUE(r_is_ok) TYPE abap_bool,
      release_ok
        IMPORTING i_trkorr       TYPE trkorr
        RETURNING VALUE(r_is_ok) TYPE abap_bool,
      store_pre_decom_version
        IMPORTING i_it_e071 TYPE e071_t,
      delete_empty_task_request_ok
        IMPORTING i_trkorr       TYPE trkorr
        RETURNING VALUE(r_is_ok) TYPE abap_bool.
    CONSTANTS: con_allowed_trfunctions_req TYPE string VALUE 'KW'    " workbench&customizing request
             , con_allowed_trfunctions_task TYPE string VALUE 'QSXR' " workbench&customizing task, unclassified (empty), repair
             .
ENDCLASS.

START-OF-SELECTION.
  lcl=>main( ).

CLASS lcl IMPLEMENTATION.

  METHOD main.
    IF s_trans IS INITIAL.
      WRITE / |Selection criteria for transports missing|.
      EXIT.
    ENDIF.

    SELECT trkorr FROM e070
      WHERE trkorr IN @s_trans
      INTO TABLE @DATA(it_trkorr).
    IF sy-subrc <> 0.
      WRITE / |No transports found with selection criteria|.
    ENDIF.

    LOOP AT it_trkorr ASSIGNING FIELD-SYMBOL(<wa_trkorr>).
      IF prepare_trkorr_ok( <wa_trkorr>-trkorr ).
        process_trkorr( <wa_trkorr>-trkorr ).
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

  METHOD process_trkorr.
    DATA: return_msg TYPE bapiret2
        , msg TYPE string
        , it_e071 TYPE e071_t
        .

    SELECT * FROM e071
      WHERE trkorr = @i_trkorr
       " SCN Version 4 of program (ZS_DECOM_OBSOL_TRANS) had another line here, which is a tiny bug
       INTO TABLE @it_e071.

    IF sy-subrc <> 0.
      " new 18.07.2016: automatically delete empty transport requests
      IF delete_empty_task_request_ok( i_trkorr = i_trkorr ).
        WRITE |Empty transport request - deleted|.
      ELSE.
        MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO msg.
        WRITE |FAILED deleting empty transport { i_trkorr }: { msg }, subrc { sy-subrc }|.
        RETURN.
      ENDIF.
      RETURN.
    ENDIF.

    DELETE it_e071 WHERE pgmid = 'CORR'.   " ignore log entries CORR RELE and CORR MERG

    SORT it_e071 BY                                   pgmid object obj_name objfunc lockflag gennum lang activity.
    DELETE ADJACENT DUPLICATES FROM it_e071 COMPARING pgmid object obj_name objfunc lockflag gennum lang activity.

    SELECT * FROM e071k
      WHERE trkorr = @i_trkorr
      INTO TABLE @DATA(it_e071k). "#ec ci_subrc

    CALL FUNCTION 'ZS_DEV_APPEND_TO_TR' DESTINATION p_p_sys
      EXPORTING
        i_it_e071             = it_e071
        i_it_e071k            = it_e071k
        i_trkorr              = p_p_tr
      IMPORTING
        e_wa_return_msg       = return_msg
      EXCEPTIONS
        communication_failure = 1
        system_failure        = 2.
    IF sy-subrc <> 0.
      WRITE |Problem with RFC call to system. Sy-subrc = { sy-subrc }|.
      EXIT.
    ENDIF.

    IF return_msg IS NOT INITIAL.
      MESSAGE ID return_msg-id TYPE return_msg-type NUMBER return_msg-number
              WITH return_msg-message_v1 return_msg-message_v2 return_msg-message_v3 return_msg-message_v4 INTO msg.
      WRITE |FAILED: { msg }|.
    ELSE.
      store_pre_decom_version( it_e071 ).
      WRITE |finished|.
    ENDIF.

  ENDMETHOD.

  METHOD prepare_trkorr_ok.
    DATA msg TYPE string.
    r_is_ok = abap_false.
    WRITE / |{ i_trkorr } |.
    SELECT SINGLE trfunction, trstatus FROM e070
      WHERE trkorr = @i_trkorr
      INTO ( @DATA(trfunction), @DATA(trstatus) ).
    ASSERT sy-subrc = 0.
    IF trfunction NA con_allowed_trfunctions_req.
      WRITE |Wrong type: E070-TRFUNCTION = { trfunction }. Allowed: { con_allowed_trfunctions_req }|.
      RETURN.
    ENDIF.
    IF trstatus <> 'D'. " changeable
      WRITE |Wrong status: E070-TRSTATUS = { trstatus }. Needs to be D (changeable)|.
      RETURN.
    ENDIF.
    SELECT trkorr, trfunction, trstatus FROM e070
      WHERE strkorr = @i_trkorr
      ORDER BY trkorr
      into table @DATA(it_dependent). "#ec ci_subrc
    LOOP AT it_dependent ASSIGNING FIELD-SYMBOL(<dep>).
      IF <dep>-trfunction NA con_allowed_trfunctions_task.
        WRITE |Wrong type: E070-TRFUNCTION = { <dep>-trfunction } of dependent transport { <dep>-trkorr }. Allowed: { con_allowed_trfunctions_task }|.
        RETURN.
      ENDIF.

      " new 18.07.2016: automatically delete unclassified, empty transport tasks
      IF <dep>-trfunction = 'X'.  " unclassified
        IF NOT delete_empty_task_request_ok( i_trkorr = <dep>-trkorr ).
          MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
                  WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO msg.
          WRITE |FAILED deleting empty dependent transport { <dep>-trkorr }: { msg }, subrc { sy-subrc }|.
        ENDIF.
        CONTINUE.
      ENDIF.

      CASE <dep>-trstatus.
        WHEN 'L' OR 'O'. " changeable locked, or release in progress
          WRITE |Wrong status: E070-TRSTATUS = { <dep>-trstatus } of dependent transport { <dep>-trkorr }. Allowed: D, R or N (changeable or released)|.
          RETURN.
        WHEN 'R' OR 'N'.
          CONTINUE.
        WHEN 'D'. " changeable
          IF release_ok( <dep>-trkorr ).
            CONTINUE.
          ELSE.
            RETURN. " error message has already been written
          ENDIF.
        WHEN OTHERS.
          ASSERT 1 = 0.
      ENDCASE.
    ENDLOOP.

    r_is_ok = prevent_inactive_objects_ok( i_trkorr ).
  ENDMETHOD.

  METHOD prevent_inactive_objects_ok.
    " Warn if there are any inactive objects.
    " Return true if everything is active.
    DATA: it_log  TYPE STANDARD TABLE OF sprot_u
        , it_e071 TYPE trwbo_t_e071
        , msg_str TYPE string
        .
    SELECT SINGLE * FROM e070
      WHERE trkorr = @i_trkorr
      INTO @DATA(e070).
    ASSERT sy-subrc = 0.

    SELECT * FROM e071
      WHERE trkorr = @i_trkorr
      INTO CORRESPONDING FIELDS of TABLE @it_e071.
    IF sy-subrc = 0.
      CALL FUNCTION 'TRINT_CHECK_INACTIVE_OBJECTS'
        EXPORTING
          is_e070 = e070
          it_e071 = it_e071
        TABLES
          et_log  = it_log.
    ENDIF.

    r_is_ok = abap_true.
    LOOP AT it_log ASSIGNING FIELD-SYMBOL(<wa_log>).
      IF sy-tabix = 1.
        WRITE |Contains inactive objects: |.
      ELSE.
        WRITE |, |.
      ENDIF.
      IF <wa_log>-severity = 'E' AND <wa_log>-ag = 'EU' AND <wa_log>-msgnr = 829.
        WRITE |{ <wa_log>-var1 } { <wa_log>-var2 }|.
      ELSE.
        MESSAGE ID <wa_log>-ag TYPE <wa_log>-severity NUMBER <wa_log>-msgnr WITH <wa_log>-var1 <wa_log>-var2 INTO msg_str.
        WRITE |"{ msg_str }"|.
      ENDIF.
      r_is_ok = abap_false.
    ENDLOOP.
  ENDMETHOD.

  METHOD release_ok.
    DATA msg TYPE string.
    CALL FUNCTION 'TR_RELEASE_REQUEST'
      EXPORTING
        iv_trkorr                  = i_trkorr
        iv_dialog                  = abap_false
        iv_success_message         = abap_false
        iv_display_export_log      = abap_false
      EXCEPTIONS
        cts_initialization_failure = 1
        enqueue_failed             = 2
        no_authorization           = 3
        invalid_request            = 4
        request_already_released   = 5
        repeat_too_early           = 6
        error_in_export_methods    = 7
        object_check_error         = 8
        docu_missing               = 9
        db_access_error            = 10
        action_aborted_by_user     = 11
        export_failed              = 12
        OTHERS                     = 13.
    IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 INTO msg.
      WRITE |FAILED releasing dependent transport { i_trkorr }: { msg }, subrc { sy-subrc }|.
      r_is_ok = abap_false.
    ELSE.
      r_is_ok = abap_true.
    ENDIF.
  ENDMETHOD.


  METHOD store_pre_decom_version.
    " this function module stores a version of all versionable
    " objects, as if the current version had just been released
    " with a transport request
    CALL FUNCTION 'SVRS_STORE_VERSION'
      EXPORTING
        date     = sy-datum
        time     = sy-uzeit
        user     = sy-uname
        korr     = 'PRE_DECOMM'
        mode     = ''
      TABLES
        e071_tab = i_it_e071.
  ENDMETHOD.

  METHOD delete_empty_task_request_ok.
    SELECT @abap_true FROM e071
      WHERE trkorr = @i_trkorr
      INTO @DATA(has_content)
      UP TO 1 ROWS.
    ENDSELECT.
    ASSERT has_content = abap_false.
    CALL FUNCTION 'TR_CHANGE_USERNAME'
      EXPORTING
        wi_dialog           = abap_false
        wi_trkorr           = i_trkorr
        wi_user             = sy-uname
      EXCEPTIONS
        already_released    = 1
        e070_update_error   = 2
        file_access_error   = 3
        not_exist_e070      = 4
        user_does_not_exist = 5
        tr_enqueue_failed   = 6
        no_authorization    = 7
        wrong_client        = 8
        unallowed_user      = 9
        OTHERS              = 10.
    IF sy-subrc = 0.
      CALL FUNCTION 'TR_DELETE_COMM'
        EXPORTING
          wi_dialog                     = abap_false
          wi_trkorr                     = i_trkorr
        EXCEPTIONS
          file_access_error             = 1
          order_already_released        = 2
          order_contains_c_member       = 3
          order_contains_locked_entries = 4
          order_is_refered              = 5
          repair_order                  = 6
          user_not_owner                = 7
          delete_was_cancelled          = 8
          ordernumber_empty             = 9
          tr_enqueue_failed             = 10
          objects_free_but_still_locks  = 11
          order_lock_failed             = 12
          no_authorization              = 13
          wrong_client                  = 14
          project_still_referenced      = 15
          successors_already_released   = 16
          OTHERS                        = 17.
    ENDIF.
    r_is_ok = xsdbool( sy-subrc = 0 ).
  ENDMETHOD.

ENDCLASS.

 

* PART 1: Top-Include of Function Pool (Include LZS_DEV_TOOLS_RFCTOP)
FUNCTION-POOL zs_dev_tools_rfc MESSAGE-ID zs_dev_tools.

CLASS lcl DEFINITION FINAL.
  PUBLIC SECTION.
    CLASS-METHODS:
        append_to_tr
          IMPORTING i_trkorr        TYPE  trkorr
                    i_it_e071       TYPE e071_t
                    i_it_e071k      TYPE e071k_t
          EXPORTING e_wa_return_msg TYPE bapiret2.

  PRIVATE SECTION.
    CLASS-METHODS:
      check_transport
        IMPORTING i_trkorr        TYPE trkorr
        EXPORTING e_is_ok         TYPE abap_bool
        CHANGING  c_wa_return_msg TYPE bapiret2,
      get_next_as4pos
        IMPORTING i_trkorr TYPE trkorr
        RETURNING VALUE(r) TYPE e071-as4pos,
      determine_as4pos
        CHANGING
          c_it_e071k TYPE e071k_t.
ENDCLASS.





* PART 2: Function Module (Include LZS_DEV_TOOLS_RFCU01)
* Note that this function module has to be created as an RFC-enabled function
FUNCTION ZS_DEV_APPEND_TO_TR
  IMPORTING
    VALUE(I_IT_E071) TYPE E071_T
    VALUE(I_IT_E071K) TYPE E071K_T
    VALUE(I_TRKORR) TYPE TRKORR
  EXPORTING
    VALUE(E_WA_RETURN_MSG) TYPE BAPIRET2.



  lcl=>append_to_tr( EXPORTING i_it_e071 = i_it_e071
                               i_it_e071k = i_it_e071k
                               i_trkorr = i_trkorr
                     IMPORTING e_wa_return_msg = e_wa_return_msg ).

ENDFUNCTION.





* PART 3: Local Class implementation (Include LZS_DEV_TOOLS_RFCP01)
CLASS lcl IMPLEMENTATION.

  METHOD append_to_tr.
    DATA: it_e071 TYPE e071_t
        , it_e071k TYPE e071k_t
        , wa_e071 TYPE e071
        .
    check_transport( EXPORTING i_trkorr = i_trkorr
                     IMPORTING e_is_ok = DATA(is_ok)
                     CHANGING c_wa_return_msg = e_wa_return_msg ).
    CHECK is_ok = abap_true.

    LOOP AT i_it_e071 ASSIGNING FIELD-SYMBOL(<wa_e071>).
      MOVE-CORRESPONDING <wa_e071> TO wa_e071.
      wa_e071-trkorr = i_trkorr.
      wa_e071-as4pos = get_next_as4pos( i_trkorr ).
      wa_e071-objfunc = COND #( WHEN wa_e071-objfunc = 'K' THEN 'K'  " entry has key records in E071K
                                ELSE '' ).                           " other values are only relevant in source system
      CLEAR wa_e071-lockflag.            " only relevant in source system
      APPEND wa_e071 TO it_e071.
    ENDLOOP.
    INSERT e071 FROM TABLE it_e071.
    ASSERT sy-subrc = 0.

    it_e071k = VALUE #( FOR wa_e071k IN i_it_e071k ( VALUE #( BASE wa_e071k
                                                              trkorr = i_trkorr ) ) ).
    determine_as4pos( CHANGING c_it_e071k = it_e071k ).
    INSERT e071k FROM TABLE it_e071k.
    ASSERT sy-subrc = 0.  " there are potential conflicts, if the same e071k entries are added via this method again. The method should not be used like this

    COMMIT WORK.

  ENDMETHOD.


  METHOD check_transport.
    e_is_ok = abap_false.
    CLEAR c_wa_return_msg.
    SELECT SINGLE @abap_true FROM e070 INTO @e_is_ok
      WHERE trkorr = @i_trkorr
        AND trstatus = 'D'.  "#ec ci_subrc      D = changeable
    IF e_is_ok = abap_false.
      MESSAGE e001 WITH i_trkorr INTO c_wa_return_msg-message. " transport not found or not suitable
      c_wa_return_msg-type = sy-msgty.
      c_wa_return_msg-id = sy-msgid.
      c_wa_return_msg-number = sy-msgno.
      c_wa_return_msg-message_v1 = sy-msgv1.
      c_wa_return_msg-message_v2 = sy-msgv2.
      c_wa_return_msg-message_v3 = sy-msgv3.
      c_wa_return_msg-message_v4 = sy-msgv4.
    ENDIF.
  ENDMETHOD.


  METHOD get_next_as4pos.
    STATICS: last_trkorr TYPE trkorr
           , last_as4pos TYPE e071-as4pos
           .
    IF last_trkorr <> i_trkorr.
      SELECT MAX( as4pos ) FROM e071
        WHERE trkorr = @i_trkorr
        INTO @last_as4pos.
      ASSERT sy-subrc = 0. " always 0 for aggregate functions
      last_trkorr = i_trkorr.
    ENDIF.
    r = last_as4pos + 1.
    last_as4pos = r.
  ENDMETHOD.


  METHOD determine_as4pos.
    LOOP AT c_it_e071k INTO DATA(wa_temp) GROUP BY ( trkorr   = wa_temp-trkorr
                                                     pgmid    = wa_temp-pgmid
                                                     object   = wa_temp-object
                                                     objname  = wa_temp-objname )
                                          ASSIGNING FIELD-SYMBOL(<group>).
      SELECT MAX( as4pos ) FROM e071k
        WHERE trkorr  = @<group>-trkorr
          AND pgmid   = @<group>-pgmid
          AND object  = @<group>-object
          AND objname = @<group>-objname
          INTO @DATA(last_as4pos).
      ASSERT sy-subrc = 0. " always 0 for aggregate functions
      LOOP AT GROUP <group> ASSIGNING FIELD-SYMBOL(<wa_e071k>).
        <wa_e071k>-as4pos = last_as4pos + 1.
        last_as4pos = <wa_e071k>-as4pos.
      ENDLOOP.
    ENDLOOP.
  ENDMETHOD.

ENDCLASS.


To report this post you need to login first.

20 Comments

You must be Logged on to comment or reply to a post.

  1. Andreas Spix

    Hi Edo,

    you are allowed to create transports in the productive system? To create a transport the system have to be “changeable”, right? But the productive system isn’t changeable.

    Andi

    (0) 
    1. Edo von Glan Post author

      Hi Andi,

      it is allowed to create transports of type “transport of copies” in non-changeable systems (at least in our installation).

      Best regards,

      Edo

      (0) 
  2. Matt Fraser

    Hi Edo,

    You’re absolutely right that this is a constant headache for Basis admins, trying to keep all the “abandoned” transports straight, and from the description this seems a great solution. I have one clarifying question.

    You mentioned that this captures customizing as well as development objects. From this, I take it you mean that it will capture table entries in customizing tables and compare them to DEV, adding the changed entries to the ToC transport in PRD?

    Good work!

    Cheers,

    Matt

    (0) 
    1. Edo von Glan Post author

      Hi Matt,

      the table keys (DB table E071K) are processed in the same way as the object keys (E071): everything that is contained in the abandoned development system transport, is put into the mirror transport on the production system. No actual comparison is performed, but it works as expected.

      Cheers,

      Edo

      PS: Thanks to everyone for the kind words. I suddenly had the idea yesterday to add a photo to my profile. Must be the facebook effect 😉

      (0) 
  3. Jelena Perfiljeva

    I’m not a native English speaker but we usually call such change requests/TRs “abandoned” and the process of removing changes – “to roll back”. Not sure if there is an official terminology. Decommissioning usually means removing something that was used for a while. Google says it means “withdraw (someone or something) from service” and in this case it’s something that never really was “in service” (never went to PRD), as far as I understand.

    Such changes are a pain in the back in our organization as well (and we have no Basis, so guess who has to sort out this stuff). Will check out the reports, looks interesting. Now if only someone could figure out a way to force the darn users to follow up on their tickets… 🙂

    Thanks for sharing!

    (0) 
  4. Gregor Wolf

    Hi Edo,

    are you aware of abapGit? I think it would be helpful if you could share your code using that tool on GitHub. That way your project has automatically an incident management and you can also define a license for it.

    Best regards

    Gregor

    (0) 
    1. Edo von Glan Post author

      Hi Gregor,

      I am aware of abapGit, but avoided it on purpose, because I did not want to spend a lot of time (and research the legal details of license models). The coding is just 300 lines.

      Best regards,

      Edo

      (0) 
  5. Daniel Klein

    I am trying to adapt the coding for Netweavers < 7.40 SP08, but the last piece of coding is a bit tricky. Can someone translate it into the ‘old’ world?

      METHOD determine_as4pos.

        LOOP AT c_it_e071k INTO DATA(wa_temp) GROUP BY ( trkorr   = wa_temp-trkorr

                                                         pgmid    = wa_temp-pgmid

                                                         OBJECT   = wa_temp-object

                                                         objname  = wa_temp-objname )

                                              ASSIGNING FIELD-SYMBOL(<group>).

          SELECT MAX( as4pos ) FROM e071k

            WHERE trkorr  = @<group>-trkorr

              AND pgmid   = @<group>-pgmid

              AND OBJECT  = @<group>-object

              AND objname = @<group>-objname

              INTO @data(last_as4pos).

          ASSERT sy-subrc = 0. ” always 0 for aggregate functions

          LOOP AT group <group> assigning FIELD-SYMBOL(<wa_e071k>).

            <wa_e071k>-as4pos = last_as4pos + 1.

            last_as4pos = <wa_e071k>-as4pos.

          ENDLOOP.

        ENDLOOP.

      ENDMETHOD.

    I could provide a SAPLink nugget if everything works.

    Thanks and Regards

    Daniel

    (0) 
    1. Edo von Glan Post author

      data: last_as4pos type i.

      field-symbols: <wa_e071k> like line of c_it_e071k.

      SORT c_it_e071k BY trkorr pgmid object objname.

      LOOP AT c_it_e071k ASSIGNING <wa_e071k>.

        AT NEW objname.

          SELECT MAX( as4pos ) FROM e071k

            INTO last_as4pos

            WHERE trkorr  = <wa_e071k>-trkorr

              AND pgmid   = <wa_e071k>-pgmid

              AND object  = <wa_e071k>-object

              AND objname = <wa_e071k>-objname.

          ASSERT sysubrc = 0. ” always 0 for aggregate functions

        ENDAT.

        <wa_e071k>-as4pos = last_as4pos + 1.

        last_as4pos = <wa_e071k>-as4pos.

      ENDLOOP.

      (0) 
      1. Juwin Pallipat Thomas

        I have never tried this method, hence can’t confirm. If you have tried this, can you please confirm if this works, in the scenario mentioned below?

        If an object was never moved to Prod (was developed, was QAed but then abandoned), I guess it may not be possible to reverse that using this methodology. Also, if that particular object was a DDIC change (eg. append structure)/ Enhancement (new BADI/ User Exit), my understanding is it will probably be impossible to get it reversed using a TR.

        I feel that this method may be a quick fix for many things, but as a long term solution to keep systems in sync, system refresh activity has to be done periodically.

        Thanks,

        Juwin

        (0) 
        1. Edo von Glan Post author

          Hi Juwin,

          You can also use it to clean up your QA system (in fact, this is how I tested the method).

          But still, I think it is advisable to periodically refresh your QA system as a copy from the Prod system.

          I think that all changes (DDIC, new BAdI, etc.) can be reverted using my method, because it is based on transports (and transports are the >1,000,000 times proven method of transporting changes of whatever kind reliably between SAP systems). I tested the following cases:

          – new / changed / deleted code

          – new / changed / deleted customizing

          – new / changed / deleted modification

          – new / changed / deleted translated texts (I noticed what seems like a bug in SAP standard in this area. Will open a ticket)

          But as I said above, I don’t mean to guarantee it. So test for yourself and post your experiences here.

          Best regards, Edo

          (0) 
  6. Daniel Klein

    Hi Edo,

    thank you for your effort, here is my feedback:

    • Step 2: please mention (again) that the report has to be executed in DEV-system. I falsely tried to execute it in P.
    • Coding of the report: comment “list of transports to be rolled back” belongs to s_trans and not to p_p_tr.
    • Could you add description proposals for the selection screen elements?
    • Coding of the function module: please add a comment that the function module has to be set to ‘RFC’ mode. It should be clear if you think about it, however, I forgot to make this setting.
    • Report: Check the RFC destination validity before doing any changes.
    • Report: Deny ‘s_trans’ to be empty, or show a popup to confirm. I accidentally released all open tasks in the DEV-system 😀
    • I could not release the transport of copies, because of error message TK420. This is because the DEV system has client 500 and the P system has 010. The customizing is added as client 500 to this transport, and therefore cannot be released in client 010. Any chance to edit the client while adding objects to the transport?

    Regards

    Daniel

    (0) 
    1. Edo von Glan Post author

      Hi Daniel,

      thanks a lot for your detailed feedback!

      I improved the code and blog text accordingly.

      Regarding your last point (different client): This could be implemented:

      • add a selection parameter “target client” to the report
      • pass the parameter to the RFC function
      • in the RFC, for all E071K entries, check the existence/position of the client in field tabkey (maybe using position info from function DDIF_NAMETAB_GET), depending on field objname (table or view name), and replace with the parameter, before adding the entry to the transport. This is the most difficult part. (I would ASSERT in the code, that the 3 characters to be replaced at the calculated position are identical to the source client.)

      However, I have no time to implement and test this, as my company does not have that situation.

      Best regards,

      Edo

      (0) 
  7. Edo von Glan Post author

    Hi Florian,
    as far as I understand, attachments are not possible in the new SCN. I will try to insert the code into the blog (which is much easier in the new SCN).
    Best regards,
    Edo

    (0) 

Leave a Reply