Quantcast
Channel: SCN : Blog List - ABAP Development
Viewing all 943 articles
Browse latest View live

IDOCs: Useful Tips to remember while configuration

$
0
0

I am sharing few not often used tips of IDOCs in this article. Generally i have seen people trying to achieve many things programmatically by coding in the user exits etc. While we have some SAP standard ways of achieving them like idoc Views,encryption,acknowledgements etc which can easily be achieved by configuration.

 

Suppressing unwanted segments-The standard Idoc types are assigned to various message types and applications. An idoc may contain various segments which are not required in some scenarios. Also, sometimes some unwanted data is sent to other applications. We may manually suppress the unwanted segments inside some user exit of the processing function module through ABAP code. But there can be some consequences of using this technique and this may not be a reliable method. Instead of this approach, Standard SAP provides reduced message type and Idoc View techniques to achieve the same solution.

 

  • Reduced Message types- we can create a Z message type with reference to a few standard message types where we can remove the unwanted segments while using the standard function module for idoc processing. The below steps illustrates the implementation of the reduced message type.
    • Open T-code BD53 and specify the reduced Z message type with the reference message type.
    • Now select or deselect the segments as per the requirement on the next screen.

                    screen1.jpg

  • Idoc Views - Idoc views is a similar technique to suppress the unwanted idoc segments. It supports more message types and basic types. The below steps illustrates the implementation of the idoc views
    • Open t-code WE32 and specify the idoc view name.
    • Provide the Message type, Basic type and Extension. Extension name is not mandatory
    • Select the segments and child segments from the next screen and save the view.

     screen2.png

Specify the view name in the partner profile while doing the configuration.

 

view.jpg

Basic Level data encryption in IDOCs- Anidoc can contain some sensitive information like credit card details of customer, phone numbers, pricing information etc. If we want this data to be encrypted for display purpose only. We can encrypt some data in the Idocs for display purpose only, like the customer care representative can see the last 5 characters of the credit card number or phone number (Phone XXXXX-53108).The transaction display authorization to some particular users can be managed by standard idoc authorization objects but if we don't want some particular fields in segments to be displayed to the users. We can use transaction WECRYPTDISPLAY.

 

idocs.JPG

This transaction is just a maintenance view of table EDCRYPTDISPLAY. We can maintain particular segments of an idoc in this transaction. Once data is maintained, In all the display transactions like WE02, WE05,WE07, WE09,WE10 etc. This sensitive data will be displayed as '*'.

encrypt.png


We can also change the masked character by creating a custom function module. This method has limitations that the data is actually not encrypted in the database. Anyone can check the correct values in the database table if they have the access.

 

Change IDOC Data/Control parameters programmatically- The failed Idoc can be reprocessed from the transaction BD87. Like, if an idoc fails due to master data issue, we can correct the master data and then reprocess the idoc. But there is a limitation - we cannot change the idoc data for reprocessing. If our idoc has wrong data, we cannot reprocess it as it will fail once again.

 

*** try at your own risk***

 

So for few idoc status, we can programmatically change the contents of an IDOC, to change the data of an IDOC follow the below steps.

  • Call FM EDI_DOCUMENT_OPEN_FOR_EDIT to open the idoc for editing. Remember we can't change all idoc status and STATUS_IS_UNABLE_FOR_CHANGING Exception will be raised in case the status is not editable.
  • Read the data returned by the above FM and make the changes to the data.
  • Call FM EDI_CHANGE_DATA_SEGMENT importing the changed data.
  • Call FM to EDI_DOCUMENT_CLOSE_EDIT to commit the changes. Once this FM is called the idoc data will be changed and the changes will be committed in the database. The status of the idoc will be changed to 69 (IDoc was edited).

idoc2.png

If we want to add the idoc to the inbound queue directly after editing. While calling the FM EDI_DOCUMENT_CLOSE_EDIT we can set  'status_75' flag as X. The Idoc status will change to 75 and the IDoc will be added to the queue.

We can also use EDI_CHANGE_CONTROL_SEGMENT to change the control data.

 

Idoc Acknowledgement for ALE and EDI scenarios

Idoc acknowledgement is an idoc by receiver just to inform the sender about the state of the outbound Idoc. The acknowledgment Idoc changes the status of the outbound idoc of the sender to a particular status depending upon negative or a positive acknowledgement. There are several Idoc status which can tell the state of the outbound idoc in the receiver system.

acknowledgement.JPG

SAP has provided different idocs for ALE and EDI Scenarios.

  • STATUS IDoc -  We can use the STATUS idoc for EDI scenario. The message type is STATUS and Idoc type is SYSTAT01. We have 2 standard inbound process codes STA1 and STA2 for IDOC processing. This Idoc has only one segment E1STATS. The acknowledging idoc number is filled in the docnum field of the E1STATS.
  • ALE AUDIT IDoc - We can use this idoc for ALE scenarios. The Message type is ALEAUD and Idoc type is ALEAUD01. A single ALE Audit idoc can contain Idoc of 500 IDocs.

 

The acknowledgement idoc changes the status of the outbound idoc. The below Idoc status can be changed by the outbound IDoc.

IDoc StatusDescription
14Interchange Acknowledgement positive
15Interchange acknowledgement negative
16Functional acknowledgement
17Functional acknowledgement negative
40Application document not created in target system
41Application document created in receiving system

Using the Standard Status Management in Custom Developments

$
0
0

A few weeks back I was debugging some issue with the status management of an order in SAP CRM. That was the first time that I noticed, that the general status management functionality is part of the core functionality of SAP Netweaver. As I like to use SAP standard whenever possible I was wondering whether it is possible to use the standard status management in custom development.

Status management in custom developments is a requirement I encountered regularly in the past. Most of the time I implemented a basic status management myself on the basis of some custom domain with numerical status values. This basic status management notable lagged most of the features the standard status management provides, like e.g.

  • the possibility to have different system and user status
  • the ability to have multiple system status active
  • or customizable and extendible status values.

 

Searching SDN I could only find little information regarding the standard status management. While there is a wiki page in the CRM area of the SDN wiki (Status Management - CRM - SCN Wiki) and a few threads on the topic (e.g. Integrating a Z Business Object with SAP Standard general status mangement?

with some very helpful comment by Rüdiger Plantiko) I couldn't find any complete example or tutorial on the topic. Therefore, I decided to create one myself. I this blog I'll provide a complete example consisting of creating a custom status object, customizing the status schema and finally using it in a simple example program.

 

Creating a custom status object

For the purpose of this blog I create a simple database table ZCD_D_STATUS_OBJ as an example business object. This table only contains the three field MANDT, OBJECT_ID and OBJECT_TEXT.

status1.png

The first thing that is needed to create a new status object for this table is a new object type including the necessary control parameters. The control parameters basically specify in which database table the custom object is stored and which are the key fields identifying an entry. For this example I create an new object type "Z1" with the value "ZCD_D_STATUS_OBJ" for the fields "Table" and "Ref.Struc." and "OBJECT_ID" for the field "Key Field":

status2.png

Next a new object type needs to be created using transaction BS12. For this example I created the object type ZT1. No further customizing is needed here for this example.

status3.png

With this basic setup in place, it is now possible to customize the status schema for our new status object. Customizing the status schema is done via transaction BS12. I created a test status schema ZCDT0001 for this example. The status schema consists only of the following three status:

  • CREA - Created
  • PROC - Processes
  • FINI - Finished.

CREA is set as the initial status value. The lowest and highest status numbers are set up in such a way that it is always only possible to go to a higher status, e.g. from PROC to FINI.

status4.png

Now all the customizing is in place to use the custom status object.

 

Test program for the custom status object

The function modules necessary to use the standard status management are locate in the function group BSVA in the package BSV. The following simple test program shows how to use these function modules to create and update the custom status object. The test program consists of the following parts:

  • First in lines 18-31 I simply use the BP number range to create an unique key for the entry in the custom table.
  • After that in lines 33-68 the custom status object for the object type ZT1 and the status schema ZCDT0001 is created.
  • In lines 70-98 the initial status values are read and printed to the output.
  • Finally in lines 100-163 some external and internal status values are set and then read and printed to the output again.

 

REPORT zcd_test_status_object.
TYPES: BEGIN OF object_key,        object_id TYPE char10,       END OF object_key.
DATA: status_test_obj TYPE zcd_d_status_obj,      object_key TYPE object_key,      status_obj_nr TYPE j_objnr,      object_type   TYPE j_obtyp,      status_schema TYPE j_stsma,      status_number TYPE j_stonr,      status_table  TYPE ttjstat,      s             TYPE string.
FIELD-SYMBOLS: <status> TYPE jstat.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Initialize the object key with an unique value
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
CALL FUNCTION 'NUMBER_GET_NEXT'  EXPORTING    nr_range_nr = '01'    object      = 'BU_PARTNER'  IMPORTING    number      = status_test_obj-object_id.
status_test_obj-object_text = 'Test Status Obj ' && status_test_obj-object_id.
INSERT zcd_d_status_obj FROM status_test_obj.
object_key-object_id = status_test_obj-object_id.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Creation of the status object
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
CALL FUNCTION 'OBJECT_NUMBER_GET_GENERIC'  EXPORTING    i_obart               = 'Z1'    i_objectkey           = object_key  IMPORTING    e_objnr               = status_obj_nr  EXCEPTIONS    number_already_exists = 1    obart_invalid         = 2    objectkey_missing     = 3    OTHERS                = 4.
IF sy-subrc <> 0.  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno              WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
CALL FUNCTION 'STATUS_OBJECT_CREATE'  EXPORTING    objnr                        = status_obj_nr    obtyp                        = 'ZT1'    stsma                        = 'ZCDT0001'  EXCEPTIONS    obtyp_invalid                = 1    status_object_already_exists = 2    stsma_invalid                = 3    stsma_obtyp_invalid          = 4    OTHERS                       = 5.
IF sy-subrc <> 0.  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno             WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
COMMIT WORK AND WAIT.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Read the initial status values and print it
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
CALL FUNCTION 'STATUS_READ'  EXPORTING    objnr            = status_obj_nr  IMPORTING    obtyp            = object_type    stsma            = status_schema    stonr            = status_number  TABLES    status           = status_table  EXCEPTIONS    object_not_found = 1    OTHERS           = 2.
IF sy-subrc <> 0.  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno             WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
WRITE / 'Initial status values'.
s = |Object Type: | && object_type && | - Status Schema: | && status_schema && | - Status Number: | && status_number.
WRITE / s.
WRITE / 'Status Table:'.
LOOP AT status_table ASSIGNING <status>.  s = |Status: | && <status>-stat && | - Inactive: | && <status>-inact.  WRITE / s.
ENDLOOP.
ULINE.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"Set some external and internal status values and print it
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
CALL FUNCTION 'STATUS_CHANGE_EXTERN'  EXPORTING    objnr               = status_obj_nr    user_status         = 'E0003'  EXCEPTIONS    object_not_found    = 1    status_inconsistent = 2    status_not_allowed  = 3    OTHERS              = 4.
IF sy-subrc <> 0.  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno             WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
CLEAR status_table.
APPEND INITIAL LINE TO status_table ASSIGNING <status>.<status>-stat = 'I0098'.
CALL FUNCTION 'STATUS_CHANGE_INTERN'  EXPORTING    objnr               = status_obj_nr  TABLES    status              = status_table  EXCEPTIONS    object_not_found    = 1    status_inconsistent = 2    status_not_allowed  = 3    OTHERS              = 4.
IF sy-subrc <> 0.  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno               WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
CLEAR status_table.
CALL FUNCTION 'STATUS_READ'  EXPORTING    objnr            = status_obj_nr   " Objektnummer  IMPORTING    obtyp            = object_type   " Objekttyp    stsma            = status_schema    " Statusschema    stonr            = status_number    " Statusordnungsnummer  TABLES    status           = status_table " Tabelle der Einzelstatus zum Objekt  EXCEPTIONS    object_not_found = 1    OTHERS           = 2.
IF sy-subrc <> 0.  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno             WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
WRITE / 'New Status values'.
s = |Object Type: | && object_type && | - Status Schema: | && status_schema && | - Status Number: | && status_number.
WRITE / s.
WRITE / 'Status Table:'.
LOOP AT status_table ASSIGNING <status>.  s = |Status: | && <status>-stat && | - Inactive: | && <status>-inact.  WRITE / s.
ENDLOOP.
ULINE.

Running this program produces the following result:

status45.png

After the status object has initially been created the user status E0001 is automatically set for the status object. This is the status that was defined in the status schema ZCDT0001. After setting the internal status I0098 and the user status E0003, the status E0001 is set to inactive.

 

I hope this blog provides an useful introduction for anyone trying to use the standard status management in a custom development.

Christian

How to update long text at line item level in FI Document ,at the time of posting using Implicit Enhancements

$
0
0

Here, for updating long text at line item level, we have 2 approaches.

1st Approach without using Implicit Enhancement

  • Post FI Document using BAPI_ACC_DOCUMENT_POST.Then collect all successfully posted documents into one internal table.
  • Then select data from BSEG table for each posted document.so that we will get line items of each posted document.
  • Then update long text for each line item using Function Module ‘CREATE_TEXT’.

2nd Approach using Implicit Enhancement

  • While posting using BAPI_ACC_DOCUMENT_POST,append line item no and its long text into EXTENSION1 Table.
  • In BAPI_ACC_DOCUMENT_POST write an implicit enhancement which will read EXTENSION1 table for all line items and store line
    item number and long text into other internal table.
  • In perform bapi_post, when document number gets created , loop on internal table of long text and update long text using
    CREATE_TEXT Function Module.

 

What is Implicit Enhancement?

The implicit enhancement is a new technology to enhance SAP’s standard objects such as includes, function modules, forms,global classes and all source code units.

The enhancement option makes it possible to add your own functionality to standard SAP objects without changing the original object. The additional functionality you added to the system by  enhancement does not create any problems while the system upgrades and can be available without making any adjustment.

 

Enhancement options are positions where we can add our code, SAP has provides with these positions where we can put our code as per the requirement, we can add an implementation element without modifying the original code of SAP.

Enhancement implantation elements are stored in the package with own transport objects thus avoiding any conflict in modification in later stages.

 

Explicit and Implicit Enhancement Options

Implicit Enhancement options are for free, provided by the framework, while the explicit ones need to be inserted explicitly, as the name indicates.

 

Sometimes you have requirements to set a default value for a standard field, or you have to add an extra column in the standard SAP List Viewer (ALV). Or you might have to include a custom include in standard code. All such requirements can be fulfilled by using implicit enhancements. Without using implicit enhancements, you have to modify the standard code, which can cause problems when upgrading your system.

 

Updating Long text using implicit enhancements

 

In bapi ‘BAPI_ACC_DOCUMENT_POST’, there is table called EXTENSION1.This table can be used to pass fields to edit the accounting document before it is transferred to the accounting components for the update.

Pass item number and its long text to EXTENSION1 table with fields

 

Extension1-field1 = ‘LONG_TEXT’

 

Extension1-field2 = long text

 

Extension1-field3 = Item no.

 

Declare global internal table by creating implicit enhancement in Global Data of BAPI_ACC_DOCUMENT _POST.

Enh1.jpg

Write Implicit Enhancement in form call_customer_function, to get line item number and its long text in
one global internal table from EXTENSION1 table.

Enh2.jpg

Write Implicit Enhancement in

FORM document_post
     USING r_compo LIKE bapiache09-compo_acc.

To update Long Text at line item level.

 

Here we will get Document number generated for posting in field gs_aw-awkey+0(10).

Enh3.jpg

In Function Module write a code to update Long Text

FUNCTION zde_fm_update_long_text.

*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(IM_ITEM) TYPE  CHAR3
*"     VALUE(IM_LONG_TEXT) TYPE  ZDE_DE_LONG_TEXT
*"     VALUE(IM_BUKRS) TYPE  BUKRS
*"     VALUE(IM_BELNR) TYPE  BELNR_D
*"     VALUE(IM_GJAHR) TYPE  GJAHR
*"  EXPORTING
*"     REFERENCE(EX_RETURN) TYPE  BAPIRET2
*"----------------------------------------------------------------------

 
"Type Declartions
 
TYPESBEGIN OF lt_type_temp,
            
line(132) TYPE c,
          
END OF lt_type_temp.

 
"Data Declarations
 
DATA : lt_temp          TYPE STANDARD TABLE OF lt_type_temp,
         lt_flines       
TYPE STANDARD TABLE OF tline,
         l_fname         
TYPE thead-tdname,
         l_length        
TYPE i,
         ls_flines       
TYPE tline,
         ls_temp         
TYPE lt_type_temp,
         l_long_text
(500) TYPE c.

 
"Constants
 
CONSTANTS : c_fid     TYPE thead-tdid     VALUE '0001',
              c_fobject
TYPE thead-tdobject VALUE 'DOC_ITEM'.

 
CONCATENATE im_bukrs
              im_belnr
              im_gjahr
              im_item
INTO l_fname.

  l_length
= strlen( im_long_text ).
  l_long_text
= im_long_text.

 
IF l_length > 132.
   
CALL FUNCTION 'SPLIT_LINE'
     
EXPORTING
       
text       = l_long_text
        len       
= l_length
        maxlen    
= '132'
     
TABLES
        result_tab
= lt_temp.

   
IF lt_temp[] IS NOT INITIAL.
     
LOOP AT lt_temp INTO ls_temp.
        ls_flines
-tdformat = '*'.
        ls_flines
-tdline   = ls_temp-line.
       
APPEND ls_flines TO lt_flines.
       
CLEAR ls_flines.
     
ENDLOOP.
   
ENDIF.
 
ELSE.

    ls_flines
-tdformat = '*'.
    ls_flines
-tdline   = im_long_text.
   
APPEND ls_flines TO lt_flines.
   
CLEAR ls_flines.
 
ENDIF.

*long Text udated line item level
 
CALL FUNCTION 'CREATE_TEXT'
   
EXPORTING
      fid        
= c_fid
      flanguage  
= sy-langu
      fname      
= l_fname
      fobject    
= c_fobject
      save_direct
= 'X'
   
TABLES
      flines     
= lt_flines
   
EXCEPTIONS
      no_init    
= 1
      no_save    
= 2
     
OTHERS      = 3.

 
IF sy-subrc = 0.
   
CLEAR: l_fname, lt_flines[].
 
ELSE.
   
IF sy-subrc &lt;> 0.

     
"Message Long Text Not Updated for Doc No &1

     
MESSAGE e054(zde_mc_message) INTO g_msg1 WITH im_belnr.

     
CALL FUNCTION 'BALW_BAPIRETURN_GET2'                  "#EC FB_RC
        
EXPORTING
          
type   = sy-msgty
           cl    
= sy-msgid
          
number = sy-msgno
           par1  
= sy-msgv1
           par2  
= sy-msgv2
           par3  
= sy-msgv3
           par4  
= sy-msgv4
        
IMPORTING
          
return = ex_return.

   
ENDIF.
 
ENDIF.
ENDFUNCTION.

 















The passion to be a (good) developer

$
0
0

In my new blog I thought I bring a theme up, which hits everyone of us.

The passion being a developer. I want to share things about my passion why I want to be a good developer at all, how to reach it and moreover how to stay there.

I mean, it is more than just having a job, going to work and earn money to live my life. For me, my work is a passion, ok sometimes there might be moments I do not got that much passion to it, but you know, all in all I’m really one of these persons, which got the luck to have a job they love.

But enough of those none catchable reasons back to the main topic.

I’m thought quite a while about this blog here and how I want to transfer my message. I mean, should I do a counting? Oh yeah, I love counting points but in this special case I’m not sure, if this is a good method. I mean, there are more than one blog out there talking about good developing and I also shared one.

Also I wasn't sure about the space, but I think this one is the best and contains a lot of people who are interested in facts like that.

 

But is this the same, good developer = passionate developer?

 

I don't think so, I’m pretty sure a very young developer with (nearly) zero expertise could also be a very passionate developer. That means, it is not the way first to get a good developer and then to find the passion. In my opinion I would say it is exact the other way. Start your job as developer and recognize your passion to it and you will get a good developer for sure.

 

It’s not rocket science all the time

 

There is also a lot of stuff which doesn't have that much passion in it. You know, it is not all the time developing cool stuff. When I used to be student I developed most of the time very cool stuff, a CD-player, games, an app with different webservices and so on. I mean I did that for getting my school-projects and because I got a lot of fun with it. The messie parts weren't in the middle of the day.

Starting my career changed it a little. Being a developer isn't all the time doing rocket science. I mean, we also have to earn our money and that means there are also parts of just getting a job done. There will be always boring tasks to do, but your job becomes more interesting over time, because you will take more senior responsibilities day for day.

Yeah, it is that easy.  Remember that thing if you got a bad day. But a very important thing is that it is also from time to time the rocket science thing. I mean you need to life your passion to not lost her (in my opinion passion is a girl, everybody should decide by it's own)

 

Lost the passion because of not having rocket science, what now?

 

Good developers might lose their passion for doing that mentioned above. I cannot understand those people, I mean as an developer we got a big advantage in our job. You know, we are part of the introducers which can show new ways of working. We are able to change thinking of people in a wise not that much people can do it. I’m not thinking about big new designs and stuff like that… not at all. I also think about the small features. You develop or enhance written source and improve it. Perhaps it is just one click not to do right now and if you got some knowledge of the industry you know what efforts are made by just saving a click

What I want to say with it:

You just have to change the perspective about the things you have to do. And if you are not happy with your things you have to do, let the team around know, that you want to get into something different. I mean, if you are not talking about things that drive you crazy you are not going to change these things.

 

I’m a developer, do I have to communicate?

 

Yes you have to. Perhaps this is the hardest part to understand, what this skill has to do with being a passionate developer. IT is not just 1 0 1 1 0 1. When I talk about my job a lot of times there is this picture drawn, that I’m a introvert guy, sitting in the basement, not having daylight and get my food delivered to not leave my chair

This perception has changed, we are considered now almost like rock-stars (ok, perhaps not that much, but you know what I want to say). Today apart from the technical skills you need to be an awesome communicator, negotiator and team player.  Normal workplace contains a team and we are part of it, isn’t it?

 

I read in an interview, that passionate developers would do their job also for free, without getting paid, just because they love it.

 

I won’t go that far, moreover I would say, that the passionate doesn’t stop at the end of the day. I think it is worse to spend some of my leisure time to explore things I’m not able to do at my working hours. But also make sure you can relax and do not try to even get 24/7 into developing. That is also not helpful…

 

Do you know a lot?

 

Whenever you think that you are wrong. And that is exact the difference between the people just doing a job and having passion for it. I mean, developing and the technology is changing too fast, but if you are passionate in your job, it doesn’t matter. I like exact that thing, that there is every day something new to learn out there. Whenever I thought there is nothing to add I always was wrong and that is great. That means, there are that much content out there, that I can spend always time to discover something I didn’t know yet.

 

That’s all Folks!


Upps, I needed a lot more words than I want to use in the beginning. But there is so much to say about and I hope you enjoyed reading it.

 

If I’m wrong with something or if you think, you have to add something, feel free to leave me a comment.

Thank you for reading to the end.

 

Cheers

Florian

 

BTW: I know that this is extensive area with a lot of different opinions but in the end I think we all driving the same highway

All pictures are out of my collection I made through my surfing. So if one or more is limited for public, let me know and I will remove it immediately.

Excel Upload Quotation - BAPI_QUOTATION_CREATEFROMDATA

$
0
0

There's no real documentation on this BAPI or on BAPI_QUOTATION_CREATEFROMDATA2. Had a lot of trouble with it considering that there was no test data in the development box. As a result I thought I'd share this.


This sample uses Conditions and Characteristics, which turned out to be very difficult to find working information on. Unfortunately no clue on how the sub-items work as it wasn't part of my requirement.


A very useful transaction to analyze any errors with is CUTRC which I never knew about. Definitely going to start using it more.


Another interesting thing that I figured out is dynamically assigning table structures to FieldSymbols. I had heard that you couldn't pass a dynamically assigned field symbol (table) to a function but mine works perfectly.


*&---------------------------------------------------------------------*
*& Report  ZUPL_QUOTATION
*&
*&---------------------------------------------------------------------*
*&
*&
*& Should've used the second BAPI. More info around on it.
*&---------------------------------------------------------------------*

REPORT zupl_quotation.
"Parameters for screen
SELECTION-SCREEN BEGIN OF BLOCK c1  WITH FRAME TITLE text-002.
* Sold-to-party, ship-to-party, Sales Organization, Distribution Channel, Division and a Sales office
PARAMETERS: p_sld_p TYPE vbak-kunnr,
             p_shp_p TYPE vbpa-kunnr,
             p_slorg TYPE vbak-vkorg,
             p_distr TYPE vbak-vtweg,
             p_div   TYPE vbak-spart,
             p_sloff TYPE vbak-vkbur,
             p_dfrom TYPE datum,
             p_dto   TYPE datum.

SELECTION-SCREEN END OF BLOCK c1.

SELECTION-SCREEN BEGIN OF BLOCK b1  WITH FRAME TITLE text-001.
PARAMETERS: r_sect RADIOBUTTON GROUP rad1 DEFAULT 'X',
             r_plat RADIOBUTTON GROUP rad1.
PARAMETERS: p_file TYPE ibipparms-path.
SELECTION-SCREEN END OF BLOCK b1.

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_file.

   CALL FUNCTION 'F4_FILENAME'
     IMPORTING
       file_name = p_file.

END-OF-SELECTION.
   "Section table
   TYPES: BEGIN OF t_datatab_s,
             quan              TYPE string,
             vc                TYPE string,
             su                TYPE string,
             ch_cu_quality     TYPE string,
             ch_cu_size        TYPE string,
             ch_cu_component   TYPE string,
             ch_zz_length      TYPE string,
             ch_zz_scrap       TYPE string,
             ch_cu_prc_opt     TYPE string,
             ch_drawing_number TYPE string,
             ch_mark_number    TYPE string,
             ch_zz_bundle_size TYPE string,
             ch_cu_time        TYPE string,
             kbetr             TYPE string,
             kpein             TYPE string,
             kmein             TYPE string,
          END OF t_datatab_s.

   "Plates table
   TYPES: BEGIN OF t_datatab_p,
             quan              TYPE string,
             vc                TYPE string,
             su                TYPE string,
             ch_ps_quality     TYPE string,
             ch_ps_size        TYPE string,
             ch_ps_component   TYPE string,
             ch_zz_length      TYPE string,
             ch_zz_width       TYPE string,
             ch_zz_scrap       TYPE string,
             ch_pl_prc_opt     TYPE string,
             ch_drawing_number TYPE string,
             ch_mark_number    TYPE string,
             ch_zz_bundle_size TYPE string,
             ch_mass_item_sn   TYPE string,
             ch_cu_time        TYPE string,
             kbetr             TYPE string,
             kpein             TYPE string,
             kmein             TYPE string,
          END OF t_datatab_p.

   "Excel Variables
   DATA: lt_filestable TYPE STANDARD TABLE OF t_datatab_s,
         lt_fileptable TYPE STANDARD TABLE OF t_datatab_p,
         ls_file_s LIKE LINE OF lt_filestable,
         ls_file_p LIKE LINE OF lt_fileptable,
         lv_file TYPE string,
         lv_rc TYPE sy-subrc,
         lv_path TYPE char255,
         it_raw TYPE truxs_t_text_data.
   "Quoatation Variables
   DATA: order_header_in TYPE bapisdhead,
         salesdocument   TYPE bapivbeln-vbeln,
         sold_to_party   TYPE bapisoldto,
         ship_to_party   TYPE bapishipto,
         billing_party   TYPE bapipayer,
         order_items_in  TYPE STANDARD TABLE OF bapiitemin WITH HEADER LINE,
         order_partners  TYPE STANDARD TABLE OF bapipartnr WITH HEADER LINE,
         order_items_out TYPE STANDARD TABLE OF bapiitemex WITH HEADER LINE,
         order_cfgs_ref  TYPE STANDARD TABLE OF bapicucfg WITH HEADER LINE,
         order_cfgs_inst TYPE STANDARD TABLE OF bapicuins WITH HEADER LINE,
         order_cfgs_part_of TYPE STANDARD TABLE OF bapicuprt WITH HEADER LINE,
         order_cfgs_value TYPE STANDARD TABLE OF bapicuval WITH HEADER LINE,
         lv_instid TYPE cu_inst_id,
         lv_itemid TYPE posnr_va,
         lv_configid TYPE cux_cfg_id.

   DATA: BEGIN OF li_return OCCURS 1.
           INCLUDE STRUCTURE bapiret1.
   DATA: END OF li_return.

   FIELD-SYMBOLS: <f_table> TYPE STANDARD TABLE,
                  <f_wa>    TYPE any,
                  <field>   TYPE any.
   DATA: it_knvp TYPE TABLE OF knvp,
         lv_parvw TYPE parvw.
   TABLES: knvp.

   lv_parvw = 'SP'.

   CALL FUNCTION 'CONVERSION_EXIT_PARVW_INPUT'
     EXPORTING
       input         = lv_parvw
     IMPORTING
       OUTPUT        = lv_parvw.

   SELECT * FROM knvp
            WHERE kunnr = p_sld_p
              AND kunn2 = p_shp_p
              AND parvw = lv_parvw.
   ENDSELECT.
   IF sy-subrc = 4.
     MESSAGE E000(ZATS_MSG) WITH 'The Ship-to-Party is not linked' 'to the selected Sold-to-Party'.
   ENDIF.
   "Radio buttons
   IF r_sect = 'X'. "Sections File
     ASSIGN lt_filestable TO <f_table>.
     ASSIGN ls_file_s TO <f_wa>.
   ELSE. "Plates File
     ASSIGN lt_fileptable TO <f_table>.
     ASSIGN ls_file_p TO <f_wa>.
   ENDIF.

   "Initialize values.
   lv_itemid = '000010'.
   lv_configid = '000001'.
   lv_instid = '00000001'.

   "Import data
   CALL FUNCTION 'TEXT_CONVERT_XLS_TO_SAP'
     EXPORTING
       i_line_header        = 'X'
       i_tab_raw_data       = it_raw
       i_filename           = p_file
     TABLES
       i_tab_converted_data = <f_table>
     EXCEPTIONS
       conversion_failed    = 1
       OTHERS               = 2.

   IF sy-subrc <> 0.
     MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
             WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
   ENDIF.

   "Mapping section from Excel file to Quoatation data.
   order_header_in-doc_type = 'ZQT1'.
   order_header_in-sales_org = p_slorg.
   order_header_in-distr_chan = p_distr.
   order_header_in-division p_div.
*  order_header_in-ref_1 = 'Your Reference Number'.
   order_header_in-qt_valid_f = p_dfrom"Quotation start date
   order_header_in-qt_valid_t = p_dto.   "Quotation end date

   order_partners-partn_role = 'SP'.
   order_partners-partn_numb = p_sld_p. "Sold to party
   CALL FUNCTION 'CONVERSION_EXIT_PARVW_INPUT' "Required as the data is stored different on the database.
     EXPORTING
       input  = order_partners-partn_role
     IMPORTING
       output = order_partners-partn_role.
   APPEND order_partners.

   order_partners-partn_role = 'SH'. "Ship to party
   order_partners-partn_numb = p_shp_p.
   CALL FUNCTION 'CONVERSION_EXIT_PARVW_INPUT' "Required as the data is stored different on the database.
     EXPORTING
       input  = order_partners-partn_role
     IMPORTING
       output = order_partners-partn_role.
   APPEND order_partners.

   LOOP AT <f_table> INTO <f_wa>.
     order_items_in-itm_number = lv_itemid. "Item Number
     order_items_in-po_itm_no = lv_itemid. "Points to the Characteristics... Without it none are populated.
     "Don't exist in this structure.
*    ORDER_ITEMS_IN-CONFIG_ID = lv_configid.
*    ORDER_ITEMS_IN-INST_ID = lv_instid.

     ASSIGN COMPONENT 'VC' OF STRUCTURE <f_wa> TO <field>.
     order_items_in-material = <field>. "Material Number
     CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
       EXPORTING
         input  = order_items_in-material
       IMPORTING
         output = order_items_in-material.

     ASSIGN COMPONENT 'QUAN' OF STRUCTURE <f_wa> TO <field>.
     order_items_in-req_qty = <field>. "Quantity
     MULTIPLY order_items_in-req_qty BY 1000.
     ASSIGN COMPONENT 'SU' OF STRUCTURE <f_wa> TO <field>.
     order_items_in-target_qu = <field>. "Measurement

     order_items_in-COND_TYPE = 'ZPRM'. "Should this be hardcoded to work?? Apparently so.
     ASSIGN COMPONENT 'KBETR' OF STRUCTURE <f_wa> TO <field>.
     order_items_in-cond_value = <field>. "Rate
     DIVIDE order_items_in-cond_value BY 10.
     ASSIGN COMPONENT 'KPEIN' OF STRUCTURE <f_wa> TO <field>.
     order_items_in-cond_p_unt = <field>. "Condition Pricing Unit
     ASSIGN COMPONENT 'KMEIN' OF STRUCTURE <f_wa> TO <field>.
     order_items_in-cond_d_unt = <field>. "Condition Unit

     APPEND order_items_in.

     "Header texts section.
     order_cfgs_value-config_id = lv_configid.
     order_cfgs_value-inst_id = lv_instid.
     order_cfgs_value-charc = 'CH_ZZ_LENGTH'.
     ASSIGN COMPONENT 'CH_ZZ_LENGTH' OF STRUCTURE <f_wa> TO <field>.
     order_cfgs_value-value = <field>.
     APPEND order_cfgs_value.

     order_cfgs_value-config_id = lv_configid.
     order_cfgs_value-inst_id = lv_instid.
     order_cfgs_value-charc = 'CH_ZZ_SCRAP'.
     ASSIGN COMPONENT 'CH_ZZ_SCRAP' OF STRUCTURE <f_wa> TO <field>.
     order_cfgs_value-value = <field>.
     APPEND order_cfgs_value.

     order_cfgs_value-config_id = lv_configid.
     order_cfgs_value-inst_id = lv_instid.
     order_cfgs_value-charc = 'CH_DRAWING_NUMBER'.
     ASSIGN COMPONENT 'CH_DRAWING_NUMBER' OF STRUCTURE <f_wa> TO <field>.
     order_cfgs_value-value = <field>.
     APPEND order_cfgs_value.

     order_cfgs_value-config_id = lv_configid.
     order_cfgs_value-inst_id = lv_instid.
     order_cfgs_value-charc = 'CH_MARK_NUMBER'.
     ASSIGN COMPONENT 'CH_MARK_NUMBER' OF STRUCTURE <f_wa> TO <field>.
     order_cfgs_value-value = <field>.
     APPEND order_cfgs_value.

     order_cfgs_value-config_id = lv_configid.
     order_cfgs_value-inst_id = lv_instid.
     order_cfgs_value-charc = 'CH_ZZ_BUNDLE_SIZE'.
     ASSIGN COMPONENT 'CH_ZZ_BUNDLE_SIZE' OF STRUCTURE <f_wa> TO <field>.
     order_cfgs_value-value = <field>.
     APPEND order_cfgs_value.

     order_cfgs_value-config_id = lv_configid.
     order_cfgs_value-inst_id = lv_instid.
     order_cfgs_value-charc = 'CH_CU_TIME'.
     ASSIGN COMPONENT 'CH_CU_TIME' OF STRUCTURE <f_wa> TO <field>.
     order_cfgs_value-value = <field>.
     APPEND order_cfgs_value.

     IF r_sect = 'X'. "Section File
       order_cfgs_value-config_id = lv_configid.
       order_cfgs_value-inst_id = lv_instid.
       order_cfgs_value-charc = 'CH_CU_COMPONENT'.
       ASSIGN COMPONENT 'CH_CU_COMPONENT' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.

       order_cfgs_value-config_id = lv_configid.
       order_cfgs_value-inst_id = lv_instid.
       order_cfgs_value-charc = 'CH_CU_PRC_OPT'.
       ASSIGN COMPONENT 'CH_CU_PRC_OPT' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.

       order_cfgs_value-config_id = lv_configid.
       order_cfgs_value-inst_id = lv_instid.
       order_cfgs_value-charc = 'CH_CU_QUALITY'.
       ASSIGN COMPONENT 'CH_CU_QUALITY' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.

       order_cfgs_value-config_id = lv_configid.
       order_cfgs_value-inst_id = lv_instid.
       order_cfgs_value-charc = 'CH_CU_SIZE'.
       ASSIGN COMPONENT 'CH_CU_SIZE' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.

     ELSEIF r_plat = 'X'. "Plates File
       order_cfgs_value-config_id = lv_configid.
       order_cfgs_value-inst_id = lv_instid.
       order_cfgs_value-charc = 'CH_PS_COMPONENT'.
       ASSIGN COMPONENT 'CH_PS_COMPONENT' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.

       order_cfgs_value-config_id = lv_configid.
       order_cfgs_value-inst_id = lv_instid.
       order_cfgs_value-charc = 'CH_PL_PRC_OPT'.
       ASSIGN COMPONENT 'CH_PL_PRC_OPT' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.

       order_cfgs_value-config_id = lv_configid.
       order_cfgs_value-inst_id = lv_instid.
       order_cfgs_value-charc = 'CH_PS_QUALITY'.
       ASSIGN COMPONENT 'CH_PS_QUALITY' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.

       order_cfgs_value-config_id = lv_configid.
       order_cfgs_value-inst_id = lv_instid.
       order_cfgs_value-charc = 'CH_PS_SIZE'.
       ASSIGN COMPONENT 'CH_PS_SIZE' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.

       order_cfgs_value-charc = 'CH_ZZ_WIDTH'.
       ASSIGN COMPONENT 'CH_ZZ_WIDTH' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.

       order_cfgs_value-charc = 'CH_MASS_ITEM_SN'.
       ASSIGN COMPONENT 'CH_MASS_ITEM_SN' OF STRUCTURE <f_wa> TO <field>.
       order_cfgs_value-value = <field>.
       APPEND order_cfgs_value.
     ENDIF.
     "End of texts (Characteristics)

     "Need to fill in order_cfgs_ref & order_cfgs_inst and possibly order_cfgs_part_of too.
     "Got the information from an example using BAPI_QUOTATION_GETDETAILBOS
     order_cfgs_ref-posex  = lv_itemid.
     order_cfgs_ref-config_id = lv_configid.
     order_cfgs_ref-root_id = lv_instid. "The type is inst_id.
     APPEND order_cfgs_ref.

     order_cfgs_inst-config_id = lv_configid.
     order_cfgs_inst-inst_id  = lv_instid.
     order_cfgs_inst-obj_type = 'MARA'.
     order_cfgs_inst-class_type = '300'.
     order_cfgs_inst-obj_key = order_items_in-material.
     order_cfgs_inst-quantity = order_items_in-target_qty.
     APPEND order_cfgs_inst.

     "Only for configurable sub-items..
*    order_cfgs_part_of-config_id = lv_configid.
*    order_cfgs_part_of-parent_id = lv_itemid.
*    order_cfgs_part_of-inst_id = lv_instid.
*    order_cfgs_part_of-class_type = '300'.
*    order_cfgs_part_of-obj_type = 'MARA'.
*    order_cfgs_part_of-obj_key = order_items_in-material.
*    APPEND order_cfgs_part_of.

     ADD 10 TO lv_itemid. "Items count in tens. Will be derived though if left open apparently.
     ADD 1 TO lv_configid. "Appears to increase per line item.
     CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
       EXPORTING
         input  = lv_itemid
       IMPORTING
         output = lv_itemid.

     CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
       EXPORTING
         input  = lv_configid
       IMPORTING
         output = lv_configid.

*    lv_instid = lv_instid + 1. "Apparently this stays 1 as per the GETDETAILBOS example.
   ENDLOOP.

   CALL FUNCTION 'BAPI_QUOTATION_CREATEFROMDATA'
     EXPORTING
       order_header_in     = order_header_in
*     without_commit      = ' '
*     convert_parvw_auart = ' '
     IMPORTING
       salesdocument       = salesdocument
       sold_to_party       = sold_to_party
       ship_to_party       = ship_to_party
*     billing_party       =
       return              = li_return
     TABLES
       order_items_in      = order_items_in
       order_partners      = order_partners
       order_items_out     = order_items_out
       order_cfgs_ref      = order_cfgs_ref
       order_cfgs_inst     = order_cfgs_inst
       order_cfgs_part_of  = order_cfgs_part_of
       order_cfgs_value    = order_cfgs_value.

   IF NOT li_return IS INITIAL.
     WRITE:/ li_return-type,
             li_return-message.
   ELSE.
     WRITE:/ 'Quotation ', salesdocument, ' created successfully.'.
   ENDIF.

The passion to be a (good) developer

$
0
0

In my new blog I thought I bring a theme up, which hits everyone of us.

The passion being a developer. I want to share things about my passion why I want to be a good developer at all, how to reach it and moreover how to stay there.

I mean, it is more than just having a job, going to work and earn money to live my life. For me, my work is a passion, ok sometimes there might be moments I do not got that much passion to it, but you know, all in all I’m really one of these persons, which got the luck to have a job they love.

But enough of those none catchable reasons back to the main topic.

I’m thought quite a while about this blog here and how I want to transfer my message. I mean, should I do a counting? Oh yeah, I love counting points but in this special case I’m not sure, if this is a good method. I mean, there are more than one blog out there talking about good developing and I also shared one.

Also I wasn't sure about the space, but I think this one is the best and contains a lot of people who are interested in facts like that.

 

But is this the same, good developer = passionate developer?

 

I don't think so, I’m pretty sure a very young developer with (nearly) zero expertise could also be a very passionate developer. That means, it is not the way first to get a good developer and then to find the passion. In my opinion I would say it is exact the other way. Start your job as developer and recognize your passion to it and you will get a good developer for sure.

 

It’s not rocket science all the time

 

There is also a lot of stuff which doesn't have that much passion in it. You know, it is not all the time developing cool stuff. When I used to be student I developed most of the time very cool stuff, a CD-player, games, an app with different webservices and so on. I mean I did that for getting my school-projects and because I got a lot of fun with it. The messie parts weren't in the middle of the day.

Starting my career changed it a little. Being a developer isn't all the time doing rocket science. I mean, we also have to earn our money and that means there are also parts of just getting a job done. There will be always boring tasks to do, but your job becomes more interesting over time, because you will take more senior responsibilities day for day.

Yeah, it is that easy.  Remember that thing if you got a bad day. But a very important thing is that it is also from time to time the rocket science thing. I mean you need to life your passion to not lost her (in my opinion passion is a girl, everybody should decide by it's own)

 

Lost the passion because of not having rocket science, what now?

 

Good developers might lose their passion for doing that mentioned above. I cannot understand those people, I mean as an developer we got a big advantage in our job. You know, we are part of the introducers which can show new ways of working. We are able to change thinking of people in a wise not that much people can do it. I’m not thinking about big new designs and stuff like that… not at all. I also think about the small features. You develop or enhance written source and improve it. Perhaps it is just one click not to do right now and if you got some knowledge of the industry you know what efforts are made by just saving a click

What I want to say with it:

You just have to change the perspective about the things you have to do. And if you are not happy with your things you have to do, let the team around know, that you want to get into something different. I mean, if you are not talking about things that drive you crazy you are not going to change these things.

 

I’m a developer, do I have to communicate?

 

Yes you have to. Perhaps this is the hardest part to understand, what this skill has to do with being a passionate developer. IT is not just 1 0 1 1 0 1. When I talk about my job a lot of times there is this picture drawn, that I’m a introvert guy, sitting in the basement, not having daylight and get my food delivered to not leave my chair

This perception has changed, we are considered now almost like rock-stars (ok, perhaps not that much, but you know what I want to say). Today apart from the technical skills you need to be an awesome communicator, negotiator and team player.  Normal workplace contains a team and we are part of it, isn’t it?

 

I read in an interview, that passionate developers would do their job also for free, without getting paid, just because they love it.

 

I won’t go that far, moreover I would say, that the passionate doesn’t stop at the end of the day. I think it is worse to spend some of my leisure time to explore things I’m not able to do at my working hours. But also make sure you can relax and do not try to even get 24/7 into developing. That is also not helpful…

 

Do you know a lot?

 

Whenever you think that you are wrong. And that is exact the difference between the people just doing a job and having passion for it. I mean, developing and the technology is changing too fast, but if you are passionate in your job, it doesn’t matter. I like exact that thing, that there is every day something new to learn out there. Whenever I thought there is nothing to add I always was wrong and that is great. That means, there are that much content out there, that I can spend always time to discover something I didn’t know yet.

 

That’s all Folks!


Upps, I needed a lot more words than I want to use in the beginning. But there is so much to say about and I hope you enjoyed reading it.

 

If I’m wrong with something or if you think, you have to add something, feel free to leave me a comment.

Thank you for reading to the end.

 

Cheers

Florian

 

BTW: I know that this is extensive area with a lot of different opinions but in the end I think we all driving the same highway

All pictures are out of my collection I made through my surfing. So if one or more is limited for public, let me know and I will remove it immediately.

Crowdsourcing of the navigation into SAP transactions

$
0
0

One of the features that I like to do to make my applications more user friendly and I dare to say that users of my applications think the same to large extent… is the quick navigation between my custom development and the SAP standard transactions. Of course SAP standard tools cannot navigate to my custom development (changing SAP standard code is not an option for me), but that does not prevent me from making the work flow, data flow and business process flow fluent and very user friendly from my side.

 

Most of the custom development pieces that I can see around me are anyway reports and UI / functionality aggregating cockpits. These two types of custom development are even more powerful when you can double-click everything you see and you’re taken to the SAP standard maintenance transaction for master data etc.

 

Because of all these reasons plus the fact that I am just one little mortal and SAP is huge and the SAP functionality is the same for everyone, it makes perfect sense to collect the information about how to make jumps to the most used transactions and share that with everyone for the benefit of everyone. I plan to start small with the navigation possibilities that I can find in my recent notes and improve this text over time if you, dear readers, can share your code for navigation with me.

 

My proposal is not a rocket science – a decent developer can find a suitable function or copy the CALL TRANSACTION (with some parameters typically) in 20 seconds (not always, read further about the “Challenge” section). What I want to do here is to create a list that will spare the developers those five minutes and will encourage the use of stable interfaces at the same time. Last but not least I want to empower the business user and application consultants that don’t know how easy or difficult it is to implement these super-user-friendly on-clicks in ABAP with something they can mail to their developers and ask them to add that to their programs in a minute.

 

I have never been a good wiki gardener and the “openness” of the wikis for everyone does not appeal to me either, so please add comments or direct_message me if you have navigation possibilities that you want to share with the team. If you have other ideas or remarks, feel free to share whatever you have, I welcome that.

 

An example what is especially welcome could be notes on security -> which navigation possibilities check the authorizations of the user and which perform the jump technically without protecting the jump (many of the examples below set some parameters and then start a transaction, that is a low brainer, but we will find some tricky cases as well). Another example could be that there is a function module that one should use for the navigation instead of some code snippets copied from SCN. Stable interfaces are always the best option if such interfaces exist.

 

Let’s also maintain a “Challenge” section. If you need to navigate to a certain transaction / screen / tool and you find it tricky and can’t find a way yourself, post it as a comment. I will then add it to the “Challenge” section and we will see if we have enough nut crackers (aka “nuts”) to find out how to do it for you. As the examples I am maintaining below to start the work rolling are not particularly difficult, I can imagine the “Challenge” section will be more fun than using SE93 with WHERE-USED list.

 

KS03 Display Cost Center

Consider one of these function modules: CK50_COSTCENTER_DISPLAY or CP_08_COSTCENTER_SHOW. There are plenty of others. SE93 -> KS03 will offer you some more options if needed. Does anyone know a simple and stable (BAPI-like) function?

 

KE53 Display Profit Center

Note: I couldn’t find a nice simple interface function module to do this.

  CALL FUNCTION 'K_KOKRS_SET'

    EXPORTING

      i_kokrs = iv_kokrs

      popup   = '0'.

 

  SET PARAMETER ID 'PRC' FIELD iv_prctr.

  CALL TRANSACTION 'KE53' AND SKIP FIRST SCREEN. “or KE52 if you wish so

 

FB03 Display Document

Consider function module FI_DOCUMENT_DISPLAY_RFC.

 

XD03 Customer Display

Consider function module BAPI_CUSTOMER_DISPLAY.

 

XK03 Vendor Display

Consider function module BAPI_VENDOR_DISPLAY.

 

MM03 Display Material

Consider function module BAPI_MATERIAL_DISPLAY.

 

CN23, KO03 Controlling Order Display

Consider function module K_ORDER_DISPLAY.

 

ME23N Display Purchase Order

Consider function module ME_DISPLAY_PURCHASE_DOCUMENT.

 

ME53N Display Purchase Requisition

Consider function module MMPUR_REQUISITION_DISPLAY.

 

 

Challenge section

No challenges yet. You’re welcome to challenge us…

Relation between Data References and Field symbols with examples

$
0
0


Hi friends,

 

The following is the blog that will explains the relationship between data refereneces and field symbols with hands on examples.

Readers will definitely get different view and indepth understanding on feild symbols and data references.

                                                           
Field Symbols


We all know the standard definition given by SAP about Field symbols as, “Field symbols are placeholders or symbolic names for other fields.
They do not physically reserve space for a field, but points to its contents. A field symbol can point to any data object. The data object to which a field
symbol points is assigned to it after it has been declared in the program”

 

Let us put the above Definition in more detailed way with
easiest examples as follows

 

Variable means!

   It will have its own address


Work area means!

  It will have its own address

 

Internal table means!

  It will have its own address


Field symbol means!

 

It will not have its own address

But it will point the address of the data object or it
will point to the content of the data object to which it has been assigned
.


So performance wise field symbols are powerful when we doing modify and append because the data update is not happening from the external memory,
instead of that it is updating from the same memory. This is how the field symbols are faster when compared to work area.

 

Note: Field symbols cannot be used without assigning a data object to them. That means either a variable or Structure or internal table
has to be assigned to field symbol before using it
.  If we remember this thumb rule it will be very easy to work with field symbols

 

1. Field symbol for a data variable

1.JPG

When we execute this program, the output of no1 is

 

2.JPG

2. Field symbol for a structure:-

 

3.JPG

 

 

When we execute the above program, it shows the MATNR, MTART as follows

 

4.JPG

3. Field symbol for an internal table

 

Suppose I have an internal table as follows

 

IT_MARA

 

Matnr      Matkl

1000        001    ----------(R1 Address of the record1 in internal table memory)

2000        002    ---------(R2 Address of the record2 in internal table memory)

 

Suppose we do have a field symbol declared as follows.

Field-symbols: <FS_MARA> TYPE MARA.

 

When we loop the above internal table by assigning to field
symbol as follows, What happens?

 

Loop at IT_MARA Assigning <FS_MARA>.

 

WRITE:  / <FS_MARA>-MATNR, <FS_MARA>-MTART.


ENDLOOP.


The memory location of each record is assigned to field symbol, that means when we work with the field symbol like changing any fields
or populating with some values nothing but we are working with the memory location of that particular record in the internal table. It is working like
“Pass by reference” in our subroutines. When we changed something in the field symbol, that means we are directly changing the internal table record.


Whereas when we have work area explicitly, the internal table record is different, and the work area we are using is different, so the
change we did in the work area has to be get modified explicitly in the internal table with the help of keyword ‘MODIFY’ unlike how field symbols
modify directly.


Example to use field symbol with internal table:-

 

5.JPG

 

 

When we execute the above program, the output is

 

6.JPG

 

 

Possible Syntaxes of field symbols:-


Read table <itab> assigning <fs>…

Loop at <itab> assigning <fs>…

Assign <data object> to <fs>

Assign component <component name/component index> to <fs>

Append initial line to <itab> assigning <fs>

 

 

 

                                                                DATA REFERENCES

 

 

Before understanding Data References, I have a question to you!


Q) What is an object / Class reference? What happens when we create a reference variable to a class?

Ans:- An object is an instance of the class. When we
create instance to the class, memory will be assigned to the components of the
class along with certain values.


Q)  What is data reference then?

Ans: - The instance of the data is called ‘Data
reference’. In simple words, memory or data will be created to the data objects
to which we want to create in the runtime.

 

 

When we go for data references?

Whenever, we don’t know clearly what data object we want to create specifically then we go for creation of data references. The data object
may be data variable, structure and internal table.

 

Types of Data References:-

 

 

  1. Known Data References
  2. Un Known Data References

 

Known Data References: When we know clearly what data type we
need to create in run time, then we go for Known data references.

 

Un Known Data References: When we don’t know what data type
that we need to create in run time, then we go for Un Known Data References.

 

Note:- It we know data type, then it doesn’t make sense to
create data reference. But before understanding Unknown data references, it is
must to understand Know creation of Data reference for known data types.

 

 

 

Syntax of Data reference:-

 

 

  1. I want to create data of integer, how?

 

7.JPG

 

 

Both the above syntaxes are same. We can go
for any syntax. I will prefer the first syntax in all the coming examples.

 

 

Before going to the examples of Known data
references and Unknown data references, let us discuss one small example which
is common to both Known and Unknown data References as follows.

 

 

DATA lr_data TYPE REF TO data.
CREATE DATA lr_data TYPE i.

 

 

What is ‘DATA’ in the above statement. ‘Data’ also a
predefined data type in ABAP like i, char10 etc but it is of Not Fully data
type which is a generic data type.

 

 

Let’s understand the above two program lines in debugging
mode.

 

 

Keep the break point in the program and understand what is
what happening with lr_Data variable.

 

8.JPG

 

 

Double click on lr_Data.

 

9.JPG

 

 

If we see the pointed area, the LR_DATA initially not
referring anything.

 

 

Now let us take F5, F5 and see the same LR_DATA.

 

10.JPG

 

 

Now if we see the LR_DATA, it is initialized to the initial
value of integer i.

That means through the statement CREATE DATA lr_Data TYPE I,
we are able to initialize the value of LR_DATA to ‘0’ from ‘INITIAL’.


Here LR_DATA is called as “Reference Variable”, not the
normal variable.

 

Now double click on ‘LR_DATA’ to understand more about
“Reference variable”.

 

 

11.JPG

 

 


Here LR_DATA, is a Reference variable

  • REF TO \TYPE=I, It is of type REF TO ‘I’
  • {A:1*\TYPE=I},  is the Address of the
    data we created


Point 1:- We cannot see the value/content of the Reference variable directly with a
double click like normal variable

 

 

To see the content/value of the Reference variable, one way is to double click on the Hook symbol.

 

12.JPG

 

 

It will show the value of the reference variable LR_DATA as
follows

 

13.JPG

 

 

 

Another way is, in debugging mode itself after double
clicking on LR_DATA, put ->* next to the LR_DATA as follows and hit enter,
it will show the value in that variable

 

 

14.JPG


By putting ->* next to a reference variable we can see the value of that
variable but we cannot assign any value into that variable


Point 2:- We need to know the meaning of two symbols ->, * .

 

-> Means Pointing the address

  1. * means Value at that address
  2. ->* Means Pointing the value
    at that address 

So as per our discussion, now let us try to assign value into the reference variable in the program as follows and see what happens and
why?

 

15.JPG

 

 

 

What is De REFERENCE means?

 

Referencing means -> Addressing certain data object by
assigning some memory to it.

 

 

 

De referencing means accessing the address (or) assigning
the value into the content of the reference.

 

-> This is referencing

->*   This is de referencing (or accessing the value at that address)

 

Here in our example de referencing of variable (or any object) is not possible through by simply adding the symbol ->* next to that
reference variable.

 

 

Then, how it is possible to assign the value into the reference variable?


Through Field-symbols only we can access the reference variables.


As field symbols are only things given by SAP, that can point the content of the address of the data
object. (Means field symbols will point to the contend of the data object, explained in the starting of this blog)


Golden Rule to Remember:-


So it is always a thumb rule to remember, to deal with accessing the address or assigning certain value into particular address, it is require to assign that
address to field symbol of that type of data object. If you don’t know the data object of what data reference we are accessing then that field symbols has to
be declared with generic data types which will be discussed later in the same blog.

 

 

DATA REFERENCES:-

 

Known Type

Un Known Type


Let us discuss the examples of Known Type Data references with Hands on examples as follows


Known Data References ( data variable creation )

 

16.JPG

 

 

Now execute the program, we will able to see that we created
an integer type variable and stored a value 10 into that through Known data
reference and a field symbol

 

17.JPG

2. Known data references(structure creation)

 

18.JPG

 

 

Now execute the program and see the values of the structure
we created through the data references

 

19.JPG

3. Known data reference(internal table creation)

 

20.JPG

21.JPG

22.JPG

 

 

Now execute the program and see, we could able to create an
internal table through data references and shown as follows. We can write
select query also as well.

 

23.JPG

 

 

Let us discuss the examples for Unknown data references.


Note:- When we working with un known data references, we definitely do not know what data object we are going to create like, we are not
sure whether we are creating an integer or character, we are not sure whether we are creating the data of MARA or VBAK, we are not sure whether we are
creating an internal table of MARA or VBAK. But it is important to know, whether we are going to create a ‘Data variable’ or ‘Structure’ or ‘Internal table’.

 

Here we need to know few Generic data types given by SAP. The generic data types are also the data types like ‘DDIC’ and ‘ABAP Program
data types’, but which are used when we don’t know the type of the data till run time.


Examples of generic data types:-


Type any (Used for both unknown variable creation, unknown structure creation)

Type any table(Used for creation of unknown table creation)

Type ref to data

Type object

 

Etc Etc

 

 

Note:- Generic data types can only be used with field symbols.

 

 

 

1. Un known data reference(creation of data
variable)

 

24.JPG

25.JPG

26.JPG

 

27.JPG

2. Un known data references(creation of structure)

 

2999.png


Now let us execute, and give your desired structure
name, our data reference creates a record from that structure and shows as
follows

29.JPG


   Enter VBAP and execute, it gives as follows

30.JPG

3. Unknown data references(creation of table):-

 

 

 

*Parameter for user input

PARAMETERS: p_var type string.



data: lr_Data type ref to data.

create data lr_Data type table of (p_var).



"As we dont know what type of structure we have to create, let us declare field symbol dynamically as follows

FIELD-SYMBOLS: <ft_table> type ANY TABLE. "key word any table is used for declaration of unknown field symbol



ASSIGN lr_Data->* to <ft_table>.

if sy-subrc is initial.

  select * from (p_var) into table <ft_table> UP TO 5 ROWS.

    if sy-subrc = 0.

      data: r_Data type REF TO cl_salv_table.

*      TRY.

      CALL METHOD cl_salv_table=>factory

*        EXPORTING

*          list_display   = IF_SALV_C_BOOL_SAP=>FALSE

*          r_container    =

*          container_name =

        IMPORTING

          r_salv_table   = r_Data

        CHANGING

          t_table        = <ft_table>

          .

*       CATCH cx_salv_msg .

*      ENDTRY.



    endif.



    r_Data->display( ).

endif.

 

Now exectute the program and give Any table name that you wish, it will show the records from that table in ALV Report.

 

This is the brief explanation of Data references with field symbols


Enforce transport Naming Conventions or Create a Sign off Procedure using CTS_REQUEST_CHECK

$
0
0

The idea behind this blog post is to promote the use of the Badi CTS_REQUEST_CHECK. Managing transports is a vital component of a successful project and often to facilitate this or to allow for traceability of changes back to tickets or projects, naming conventions for transport are mandated. However, in the absence of any systems checks these conventions are often more honoured in the breach than the keeping.

This is where Badi CTS_REQUEST_CHECK comes in extremely useful as it allows for the addition of custom code around the process of creation and release of transports. For example, if we have a naming convention that states that a transport should start with a valid module name, followed by an incident ticket number then we could create a function module that checks that an input string adheres to this convention and would raise an error otherwise. Then we would implement method CHECK_BEFORE_CREATION for badi CTS_REQUEST_CHECK and call this function module from this method displaying an error message if the text was found non compliant.

 

METHOD if_ex_cts_request_check~check_before_creation.

CALL FUNCTION 'ZABAP_TRANSPORT_TEXT'
EXPORTING
iv_text          
= text
EXCEPTIONS
naming_convention
= 1
OTHERS            = 2.
IF sy-subrc <> 0.
MESSAGE e001(ZABAP).
RAISE cancel.
ENDIF.

ENDMETHOD.


This method is called as the name suggests when a new transport is created. Of course, there are other methods of this Badi that allow for other functionality to be developed. For example by using the method CHECK_BEFORE_RELEASE (called when we try to release the transport) we can create a sign off procedure whereby more senior developers would have to sign off for all changes as a form of quality control. For example:

 

METHOD if_ex_cts_request_check~check_before_release.

DATA: lv_bname  TYPE  xuser,
lv_password
TYPE  xubcode,
ls_signoff
TYPE zabap_signoff.
CALL FUNCTION 'ZABAP_TRANSPORT_TEXT'
EXPORTING
iv_text          
= text
EXCEPTIONS
naming_convention
= 1
OTHERS            = 2.
IF sy-subrc <> 0.
MESSAGE e001(zabap).
RAISE cancel.
ENDIF.

IF type = 'K'.
IF NOT sy-uname  = 'F25850A'  AND
NOT sy-uname = 'OC1603' AND
NOT sy-uname = 'OC384'.
CALL FUNCTION 'ZPOPUP_GET_USER_PASSWORD'
EXPORTING
iv_user       
= 'F25850A'
IMPORTING
user          
= lv_bname
password      
= lv_password
EXCEPTIONS
cancel_pressed
= 1
OTHERS         = 2.
IF sy-subrc <> 0 OR lv_password IS INITIAL.
CALL FUNCTION 'ZPOPUP_GET_USER_PASSWORD'
EXPORTING
iv_user       
= 'OC1603'
IMPORTING
user          
= lv_bname
password      
= lv_password
EXCEPTIONS
cancel_pressed
= 1
OTHERS         = 2.
IF sy-subrc <> 0 OR lv_password IS INITIAL.
CALL FUNCTION 'ZPOPUP_GET_USER_PASSWORD'
EXPORTING
iv_user       
= 'OC384'
IMPORTING
user          
= lv_bname
password      
= lv_password
EXCEPTIONS
cancel_pressed
= 1
OTHERS         = 2.
IF sy-subrc <> 0 OR lv_password IS INITIAL.
MESSAGE e002(zabap).
RAISE cancel.
ENDIF.
ENDIF.
ENDIF.
CALL FUNCTION 'SUSR_LOGIN_CHECK_RFC'
EXPORTING
bname                    
= lv_bname
password                 
= lv_password
EXCEPTIONS
wait                      = 1
user_locked              
= 2
user_not_active          
= 3
password_expired         
= 4
wrong_password           
= 5
no_check_for_this_user   
= 6
password_attempts_limited
= 7
internal_error           
= 8
OTHERS                    = 9.
IF sy-subrc <> 0.
MESSAGE e003(zabap).
RAISE cancel.
ENDIF.
ls_signoff
-request = request.
ls_signoff
-uname = lv_bname.
INSERT into zabap_signoff values ls_signoff.
ENDIF.
ENDIF.

ENDMETHOD.


This also gives us an opportunity to double check the naming convention because some automatically generated transports can slip through the net of the previous check in CHECK_BEFORE_CREATION. We then check is the user releasing is one of the three authorised users and if not we require that one of these users enters a password to sign off on the changes and finally we create a log of these changes in the table zabap_signoff.

Of course may other checks are possible. I was very interested by the idea of triggering the code inspector on release of a transport that I stumbled over while preparing this blog http://scn.sap.com/community/abap/testing-and-troubleshooting/blog/2013/09/19/how-to-trigger-atc-or-code-inspector-checks-during-the-release-of-a-transport-task. As I said at the beginning the goal of this blog is just to hopefully make more people aware of this Badi and the possibilities it presents to improve the management of our transports.

XLSX Workbench

$
0
0

TITLE_21.png

PROJECT LINKS:


IDEA

For a long time only OLE/DOI-approach was available for creating spreadsheets. As you know, OLE/DOI-approach is quite flexible, but some restrictions. For example, it requires installed MS Excel application (this makes impossible to use background job for generate spreadsheet). Moreover, OLE-approach has low performance (this problem may be partly solved by using VBA, but this requires manually changing of security settings MS-Excel application on each front-end computer).

 

Since, ZIP-folder processing tools became available in the SAP environment, we are able to generate spreadsheets with new Open XML Format (XLSX). This approach does not have lacks of OLE.

 

But, in my opinion, the flaw of all existing solutions for generate spreadsheets from ABAP- is many lines of ABAP code required directly for creating and formatting spreadsheet layout. This fact is the cause of that, the same tasks are quite differently implemented by different developers, which complicates further support these developments. My idea is to create a tool (to design spreadsheets) which has graphical user interface, for example such as SMARTFORMS; and resultant spreadsheet would have XLSX format.

 

 

WHAT DOES DEVELOPMENT PROCESS LOOK LIKE ?

The development via XLSX Workbench comprises three components:

  • FORM. The principle of visual design forms in XLSX Worbkench has some similarities with the principles of using such editors as SMARTFORMS or AdobeForms. Forms are stored in the SAP WEB-repository (transaction SMW0, binary data for WebRFC-applications), and represented as XLSX-files. All data about form's structure is inside the file.

 

  • CONTEXT.  But here notion of the context is little simplified compared with the AdobeForms.

 

  • PRINTING PROGRAM. It must contain next steps:
      • Declaration of the context (int.table or structure)
      • Code for filling of the context
      • Call a funct.module 'ZXLWB_CALLFORM'

 

 

HOW TO GET RESULTANT XLSX-FILE ?

You run the print program, which prepare a source data (and fill the context) and pass it to F.M.'ZXLWB_CALLFORM'. The F.M. generates the resultant XLSX-file using the form's structure and context data. Everything is processed on the application server, but the result at the developer's decision:

  • could be displayed on the front-end computer via MS Excel application;
  • could be saved on the front-end computer as file (.XLSX) in a specified path;
  • could be returned into the printing program as rawdata.

Update VC (Variant configuration) data using standard BAPI in Sales Order

$
0
0

Updating Variant Configuration data for SO item became little tricky for us in an ongoing implementation. After spending some hours investigating the correct combination of data to pass, we were able to post the document correctly. As no detailed documentation is available online for this scenario, I hope this post will help community for similar requirements.

 

For updating Variant configuration (VC) data for Sales order item, we need to populate below tables of standard FM or BAPI (e.g. SD_SALESDOCUMENT_CREATE).

Normally the standard FM or BAPI does not return any error messages in case configuration data is not updated successfully.

·         SALES_SCHEDULES_IN: The required date field should be populated with appropriate value (REQ_DATE).

·         SALES_ITEMS_IN: Field PO_ITM_NO should be populated with appropriate value.

 

·         SALES_CFGS_REF Table:

1.       This table should have 1 record per item.

2.       Combination of CONFIG_ID and ROOT_ID should be unique across line items.

 

POSEX

000010

CONFIG_ID

000001

ROOT_ID

00000001

SCE

1

COMPLETE

T

CONSISTENT

T

CBASE_ID_TYPE

G

 

·         SALES_CFGS_INST:

 

1.       This table should have 1 record per item.

2.       Combination of CONFIG_ID and INST_ID should be unique across line items.

 

CONFIG_ID

000001

INST_ID

00000001

OBJ_TYPE

MARA

CLASS_TYPE

300

OBJ_KEY

MATNR value

QUANTITY

Quantity value

QUANTITY_UNIT

Quantity Unit

COMPLETE

T

CONSISTENT

T

OBJECT_GUID

MATNR value

PERSIST_ID_TYPE

G

 

 

 

·         SALES_CFGS_VALUE:

 

1.       Combination of CONFIG_ID and INST_ID should be unique across line items.

2.       We can have multiple characteristics for a material. In that case appropriate records should be inserted in this table. Note that CONFIG_ID and INST_ID should be same for all the rows you insert in this table for multiple characteristics for a material.

3.       The characteristic value should be in SAP internal format.

 

CONFIG_ID

000001

INST_ID

00000001

CHARC

Material characteristics

VALUE

Material characteristics value

 

 

·         SALES_CFGS_VK:

 

1.       Combination of CONFIG_ID and INST_ID should be unique across line items.

2.       We can have multiple characteristics for a material. In that case appropriate records should be inserted in this table. Note that CONFIG_ID and INST_ID should be same for all the rows you insert in this table for multiple characteristics for a material.

 

CONFIG_ID

000001

INST_ID

00000001

VKEY

Material characteristics

Favorite List Management Tool

$
0
0

My Favorite List Management guideline

 

In my daily work I like to use Favorite to make my work more efficiently. My personal guideline to management favorite list:

 

 

  1. The most frequently used tcode which are brief themselves ( e.g SE24, SE38 ) are not put into favorite list.
  2. The more frequently used, the upper position it will be in the favorite list. ( as a CRM developer, I am using BSP_WD_CMPWB every day for two years, but I still could not remember it, so I have to put it to favorite ) The less, the lower.
  3. I never use nested folder in favorite list.  Suppose I have put one tcode to deeply nested folder "debug tool", then it would take me 4 clicks to launch the tcode. I don't want to waste time on these 4 clicks. So there are no nested folder in my favorite list.

clipboard1.png

Pain points for Favorite list organization

 

In my opinion, I don't think the Favorite update functionality in SAP gui is convenient to use.

For example if I want to drag one tcode in the bottom area of the favorite tree and drop it to one upper folder,

clipboard2.png

I need to press the mouse and scroll up the favorite tree until the destination folder appears in the screen, and then drop the tcode to the folder. The scroll speed is quite low. Or I can delete the original tcode in the bottom, and scroll up to the upper folder, and re-add the tcode into that folder.

clipboard3.png

So I export the favorite list into a local text file and plan to edit that file locally.

 

Unfortunately the format of the text file is still not straightforward and difficult for manual change:

clipboard4.png

  1. each line consists of four columns:

     a. menu type: TR indicates that it is a transaction code, space means it is a folder

     b. object ID: each record must have a unique ID. And the hierarchy relationship between a folder and its wrapped tcode are represented by the ID.

     c. tcode technical name

     d. tcode descripton, or folder name

  2. It is necessary to manipulate the object ID on my own if I want to manually change the text file. Special attention must be paid if I need to move a given tcode from one folder to another folder. The corrupt file( wrong object ID ) would prevent you from seeing the favorite list at all. During my attempt, adter I uploaded the manually edited text file, I could not use the system any more since there is dump when SAPGUI tries to render the favorite list with corrupt file:

clipboard5.png

Tool development

 

So I develop a simple tool which will upload a text file with a new format defined by myself. It only has two columns:

 

Column1: the first level of favorite list tree. It could be a folder, a tcode( wrapped with "[ ]" ), or a webdynpro application( wrapped with "( )" ).

Column2: the second level of favorite list tree. It must be either a tcode or a webdynpro application. No nested folder is allowed since as I said I do not need nested folder in my daily work at all. The hierarchy relationship between a folder and its wrapped tcode are represented by the Tab key.

 

clipboard6.png


After I run the tool to upload the text file, my Favorite list is rendered with exactly the same structure defined in text file.

clipboard7.png

Benefit of using this tool:

 

  1. It is no need to manipulate object ID any more. The tool will calculate it automatically.
  2. No need to maintain the tcode description or webdynpro application description. Just maintain the tcode technical name and the tool will fetch description automatically.
  3. The text file itself is very easy to understand. If you need to move a tcode from one folder to another, just do cut & paste in text file.

 

I just upload my favorite list which contains 290 entries for your reference.

 

Further reading

 

1. If you would like to know where are these tcode from drop down list stored in your laptop, read this document.

clipboard9.png

2. Some more detail about SAP GUI settings storage could be found in this blog.

Try this out: Brand-new ABAP for SAP HANA End-to-End Development Guide

$
0
0

Hi ABAP fans!
Let me do a bit of promotion for ABAP on SAP HANA.

Here's our brand-new ABAP for SAP HANA End-to-End Development example, guiding you the way from HANA via ABAP to a SAP Fiori-like application. Within the guide, we're touching CDS Views as well as ABAP managed database procedures, two of the new features provided with AS ABAP 7.4 SP5, create SAP NetWeaver OData Services, and show the data in a (rather simple) Fiori-like application.

Interested?
Just try it out:Brand-new ABAP 7.4 for SAP HANA End to End Development Guide with Latest ABAP 7.4 SP5 Features

 

Meet you there,

  Jasmin

ABAP cross system object recursive dependencies check with transport (version 01)

$
0
0
tools.jpg

If you need to know the scope and dependencies of a repository workbench customer object and if needed at the end prepare a coherent transport request this tool is what you are looking for.

 

You can even run cross-system version checks to detect objects differences and decide what to include in your transport or analyze each object using direct object editor navigation with all standard features.

 

Since SAP objects hierarchy is complex to understand this tool will be versioned and I hope one day more objects can be added.

 

How to implement:

  • Create a executable report in your SAP development system with name ZNM_OBJS_DEP_TRANS and description Object Dependencies and Transport using transaction SE38;
  • Copy past code bellow and activate the program;
  • Create/Fill corresponding Text Symbols and Selection Texts. Please check the meaning at top comments of the program;
  • Create GUI Status with name STATUS and add standard functions &ALL, &SAL, &OUP, &ODN, &ILT, %PC, &OL0, &OAD and &AVE.

Create customer function code TR :

ex7.png

ex6.png


If you are not able to activate the program because of nonexistent standard objects please downgrade for earlier SAP versions. If you need help on it, please kept me informed.


Copy past the code bellow or download the attached file:


REPORT znm_objs_dep_trans MESSAGE-ID 00.

*----------------------------------------------------------------------*

* Report ZZS_OBJ_DEP_TRANS - Object Dependencies and Transport by Nuno Morais

*----------------------------------------------------------------------*

* Text Symbols

* B01 Object selection

* B02 Aditional options

* C01 Remote

* C02 Obj. Desc.

* CM1 Cross-system objects version check

* M01 Object not found

* M02 Please fill all required fields

* M03 Critical error

* M04 No objects selected

* M05 Transport not allowed for multiple targets

* M06 Error creating transport request

* M07 Objects added to request (

* M08 Please select a remote system

* M09 RFCs destinations missing

* M10 Request canceled, at least one object $TEMP detected

* M11 Navigation not possible

* M12 Transport canceled

* M13 Object dependencies check not support

* M14 No dependecies found

* O01 Equal

* O02 Different

* O03 New

* O04 No version

* P01 Adding object

* P02 Checking Dependecies

* P03 Checking Remote

* P04 Display objects

* P05 Processing options

* PB1 % Complete

*

* Selection Texts

* P_ERFC Exclude RFCs if exist

* P_ICD Include Tables CDs

* P_ILO Include Tables Locks

* P_ITM Include Tables Maintenances

* P_OBJECT Object Type

* P_OBJ_N Object Name

* P_PGMID Program ID

* P_RFC Remote versions checks

* P_RFC_D System Name

*

* Standard Status GUI function codes: &ALL, &SAL, &OUP, &ODN, &ILT, %PC, &OL0, &OAD and &AVE

* Status GUI function code: TR Create transport

*

*----------------------------------------------------------------------*

* GLOBAL DATA

*----------------------------------------------------------------------*

TYPE-POOLS: abap, icon. "Only for old versions

 

*----------------------------------------------------------- Constants *

CONSTANTS:

  gc_r3tr TYPE pgmid        VALUE 'R3TR', "Main object

  gc_tobj TYPE trobjtype    VALUE 'TOBJ', "Table content in transport

  gc_chdo TYPE trobjtype    VALUE 'CHDO', "Change documents

  gc_fugr TYPE trobjtype    VALUE 'FUGR', "Function group

  gc_tabl TYPE trobjtype    VALUE 'TABL', "Table

  gc_temp TYPE developclass VALUE '$TMP'. "Local development class

 

*---------------------------------------------------------- Structures *

TYPES:

  BEGIN OF gty_objects,

    status   TYPE icon_d,       "Check status

    pgmid    TYPE pgmid,        "Program ID in Requests and Tasks

    object   TYPE trobjtype,    "Object Type

    obj_name TYPE sobj_name,    "Object Name in Object Directory

    obj_desc TYPE ddtext,       "Object Explanatory short text

    devclass TYPE developclass, "Development Package

    target   TYPE tr_target,    "Transport Target of Request

    remote   TYPE char10,       "Remote check status

  END OF gty_objects.

 

SET EXTENDED CHECK OFF.

 

 

DATA:

  gt_objects   TYPE TABLE OF gty_objects, "Objects to transport

  gt_objs_desc TYPE TABLE OF ko100,       "Objects prograns IDs

  gt_e071      TYPE TABLE OF e071,

  gt_e071k     TYPE TABLE OF e071k.       "Change & Transport System: Object Entries of Requests/Tasks

 

*----------------------------------------------------------- Variables *

DATA gv_percent TYPE i. "Progress bar percentage

 

*------------------------------------------------------  Class Handler *

DATA go_objects TYPE REF TO cl_salv_table. "Objects ALV

 

SET EXTENDED CHECK ON.

 

*----------------------------------------------------------------------*

* CLASS gcl_handle_events DEFINITION

*----------------------------------------------------------------------*

CLASS lcl_handle_events DEFINITION FINAL.

 

  PUBLIC SECTION.

    METHODS:

      on_user_command FOR EVENT added_function OF cl_salv_events

        IMPORTING e_salv_function,

 

      on_double_click FOR EVENT double_click OF cl_salv_events_table

        IMPORTING row column.                               "#EC NEEDED

 

ENDCLASS.                    "lcl_handle_events DEFINITION

 

*----------------------------------------------------------------------*

* SELECTION SCREEN

*----------------------------------------------------------------------*

*---------------------------------------------------- Object selection *

SELECTION-SCREEN BEGIN OF BLOCK b01 WITH FRAME TITLE text-b01.

SELECTION-SCREEN SKIP 1.

 

SELECTION-SCREEN BEGIN OF LINE.

PARAMETERS:

  p_pgmid  TYPE pgmid DEFAULT gc_r3tr,  "Program ID in Requests and Tasks

  p_object TYPE trobjtype,              "Object Type

  p_obj_n  TYPE sobj_name.              "Object Name in Object Directory

SELECTION-SCREEN END OF LINE.

 

SELECTION-SCREEN SKIP 1.

 

SELECTION-SCREEN BEGIN OF LINE. "Remote versions checks

PARAMETERS: p_rfc AS CHECKBOX.

SELECTION-SCREEN COMMENT (37) com_rfc.

PARAMETERS: p_rfc_d TYPE tmssysnam.

SELECTION-SCREEN END OF LINE.

 

SELECTION-SCREEN END OF BLOCK b01.

 

*-------------------------------------------------- Aditional options *

SELECTION-SCREEN BEGIN OF BLOCK b02 WITH FRAME TITLE text-b02.

SELECTION-SCREEN SKIP 1.

 

PARAMETERS:

  p_itm  AS CHECKBOX, "Include Tables Maintenance

  p_ilo  AS CHECKBOX, "Include Lock objects

  p_icd  AS CHECKBOX, "Include Change documents

  p_erfc AS CHECKBOX. "Exclude RFCs if exist

 

SELECTION-SCREEN END OF BLOCK b02.

 

*--------------------------------------*

* Selection Screen PAI Helps

*--------------------------------------*

*------------------------------------------------------- Program ID F4 *

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_pgmid.

  PERFORM pgmid_f4.

*------------------------------------------------------ Object Type F4 *

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_object.

  PERFORM object_f4.

*------------------------------------------------------ Object Name F4 *

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_obj_n.

  PERFORM object_name_f4.

*-------------------------------------------------- Systems and RFC F4 *

AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_rfc_d.

  PERFORM rfc_f4.

 

*--------------------------------*

* Selection Screen PAI

*--------------------------------*

AT SELECTION-SCREEN.

  PERFORM screen_pai.

 

*----------------------------------------------------------------------*

* REPORT EVENTS

*----------------------------------------------------------------------*

*---------------------------------*

* Initialization events

*---------------------------------*

INITIALIZATION.

  PERFORM load_of_program.

 

*---------------------------------*

* Executing events

*---------------------------------*

START-OF-SELECTION.

  PERFORM run_checks.

 

END-OF-SELECTION.

  PERFORM display_objects.

 

*----------------------------------------------------------------------*

* CLASS lcl_handle_events IMPLEMENTATION                                                                                         *

*----------------------------------------------------------------------*

CLASS lcl_handle_events IMPLEMENTATION.

 

*-------------------------------------------------------- User command *

  METHOD on_user_command.

 

    CHECK e_salv_function = 'TR'.  "Create transport request

 

    DATA lr_selections TYPE REF TO cl_salv_selections. "ALV Selections

 

    DATA:

      lt_rows       TYPE salv_t_row,            "ALV Rows

      ls_row        TYPE i,

      lt_e071_temp  TYPE TABLE OF e071,         "Change & Transport System: Object Entries of Requests/Tasks

      ls_e071       LIKE LINE OF gt_e071,

      lt_e071k_temp TYPE TABLE OF e071k,

      lt_objects    TYPE TABLE OF gty_objects,  "Objects to transport

      ls_object     LIKE LINE OF gt_objects,

      lt_targets    TYPE TABLE OF tr_target,    "Transport Target of Request

      ls_target     LIKE LINE OF lt_targets.

 

    DATA:

      lv_order TYPE trkorr, "Request/Task

      lv_task  TYPE trkorr.

 

*---------- Get selected lines ----------*

    lr_selections = go_objects->get_selections( ).

    lt_rows = lr_selections->get_selected_rows( ).

 

*---------- Get selected objects to transport ----------*

    LOOP AT lt_rows INTO ls_row.

      READ TABLE gt_objects INTO ls_object INDEX ls_row.

 

      IF sy-subrc IS INITIAL AND ls_object-status = icon_led_green.

        IF ls_object-devclass = gc_temp. "Request canceled, at least one object $TEMP detected

          MESSAGE i001 WITH text-m10 space space space DISPLAY LIKE 'E'.

          RETURN.

        ENDIF.

 

        APPEND ls_object TO lt_objects.

        MOVE-CORRESPONDING ls_object TO ls_e071.

        APPEND ls_e071 TO gt_e071.

 

        IF ls_object-object = gc_tobj. "Add TABU object directly to the transport

          PERFORM add_tobj_content USING ls_object-obj_name.

        ENDIF.

      ENDIF.

    ENDLOOP.

 

*---------- Get possible target ----------*

    LOOP AT lt_objects INTO ls_object.

      ls_target = ls_object-target.

      APPEND ls_target TO lt_targets.

    ENDLOOP.

 

    SORT lt_targets.

    DELETE ADJACENT DUPLICATES FROM lt_targets.

 

*---------- Create transport request and task ----------*

    IF lt_objects IS NOT INITIAL. "Objects selected to transport

      IF lines( lt_targets ) = 1. "Only one valid target

 

        CALL FUNCTION 'TRINT_ORDER_CHOICE'  "Create transport request

          EXPORTING

            iv_tarsystem           = ls_target

          IMPORTING

            we_order               = lv_order

            we_task                = lv_task

          TABLES

            wt_e071                = lt_e071_temp

            wt_e071k               = lt_e071k_temp

          EXCEPTIONS

            no_correction_selected = 1

            display_mode           = 2

            object_append_error    = 3

            recursive_call         = 4

            wrong_order_type       = 5

            OTHERS                 = 6.

 

        IF sy-subrc IS INITIAL AND lv_task IS NOT INITIAL.

          ls_e071-pgmid    = gc_r3tr.  "Add objects development class to transport

          ls_e071-object   = 'DEVC'.

          ls_e071-obj_name = ls_object-devclass.

          APPEND ls_e071 TO gt_e071.

 

          CALL FUNCTION 'TRINT_APPEND_COMM' "Add object to transport request

            EXPORTING

              wi_exclusive       = abap_false

              wi_sel_e071        = abap_true

              wi_sel_e071k       = abap_true

              wi_trkorr          = lv_task

            TABLES

              wt_e071            = gt_e071

              wt_e071k           = gt_e071k

            EXCEPTIONS

              e071k_append_error = 1

              e071_append_error  = 2

              trkorr_empty       = 3

              OTHERS             = 4.

 

          IF sy-subrc IS INITIAL. "Added with sucess

*---------- Sort and compress request --------*

            CALL FUNCTION 'TR_SORT_AND_COMPRESS_COMM' "#EC FB_RC   "#EC CI_SUBRC

              EXPORTING

                iv_trkorr                      = lv_task

              EXCEPTIONS

                trkorr_not_found               = 1

                order_released                 = 2

                error_while_modifying_obj_list = 3

                tr_enqueue_failed              = 4

                no_authorization               = 5

                OTHERS                         = 6.

 

            MESSAGE i001 WITH text-m07 lv_order ')' space.  "Objects added to request

 

          ELSE. "Error creating transport request

            MESSAGE s001 WITH text-m06 space space space DISPLAY LIKE 'E'.

          ENDIF.

 

        ELSE. "Transport canceled

          MESSAGE s001 WITH text-m12 space space space DISPLAY LIKE 'W'.

        ENDIF.

 

      ELSE. "Transport not allowed for multiple targets

        MESSAGE i001 WITH text-m05 space space space.

      ENDIF.

 

    ELSE. "No objects selected

      MESSAGE i001 WITH text-m04 space space space.

    ENDIF.

 

  ENDMETHOD.                    "on_user_command

 

*-------------------------------------------------------- Line dbclick *

  METHOD on_double_click.

 

    DATA ls_object LIKE LINE OF gt_objects.  "Objects to transport

 

    READ TABLE gt_objects INTO ls_object INDEX row.

    IF sy-subrc IS INITIAL.

 

*---------- Display objects ----------*

      CASE ls_object-object.

        WHEN gc_tobj.  "Display Tables Maintenance

          SET PARAMETER ID 'DVI' FIELD ls_object-obj_name.

          CALL TRANSACTION 'SE54'.                       "#EC CI_CALLTA

 

        WHEN gc_chdo.  "Display change documents

          CALL TRANSACTION 'SCDO'.                       "#EC CI_CALLTA

 

        WHEN OTHERS.  "Display all

          CALL FUNCTION 'RS_TOOL_ACCESS'

            EXPORTING

              operation           = 'SHOW'

              object_name         = ls_object-obj_name

              object_type         = ls_object-object

            EXCEPTIONS

              not_executed        = 1

              invalid_object_type = 2

              OTHERS              = 3.

 

          IF sy-subrc IS NOT INITIAL. "Navigation not possible

            MESSAGE s001 WITH text-m11 space space space DISPLAY LIKE 'W'.

          ENDIF.

      ENDCASE.

    ENDIF.

 

  ENDMETHOD.                    "on_double_click

 

ENDCLASS.                    "lcl_handle_events IMPLEMENTATION

 

*----------------------------------------------------------------------*

* FORMS

*----------------------------------------------------------------------*

*&---------------------------------------------------------------------*

*&      Form  LOAD_OF_PROGRAM

*&---------------------------------------------------------------------*

FORM load_of_program .

 

  com_rfc = text-cm1. "Cross-system objects versions check

 

  CALL FUNCTION 'TR_OBJECT_TABLE' "Fill Program IDs

    TABLES

      wt_object_text = gt_objs_desc.

 

ENDFORM.                    " LOAD_OF_PROGRAM

 

*----------------------------------------------------------------------*

* Form PGMID_F4 - Program ID F4

*----------------------------------------------------------------------*

FORM pgmid_f4 .

 

  DATA lt_pgmids TYPE TABLE OF ko101.  "Program IDs with Description

 

*---------- Read PGMID ----------*

  CALL FUNCTION 'TR_PGMID_TABLE'

    TABLES

      wt_pgmid_text = lt_pgmids.

 

*---------- Set PGMID F4 ----------*

  CALL FUNCTION 'F4IF_INT_TABLE_VALUE_REQUEST'              "#EC FB_RC

    EXPORTING

      retfield        = 'PGMID'

      dynpprog        = sy-cprog

      value_org       = 'S'

      dynpnr          = '1000'

      dynprofield     = 'TRE071X-PGMID'

    TABLES

      value_tab       = lt_pgmids

    EXCEPTIONS

      parameter_error = 1

      no_values_found = 2

      OTHERS          = 3.

 

ENDFORM.                    " PGMID_F4

 

*----------------------------------------------------------------------*

* Form OBJECT_F4 - Object Type F4

*----------------------------------------------------------------------*

FORM object_f4.

 

  DATA:

    lt_shlp          TYPE shlp_descr,               "Description of Search Help

    lt_return_values TYPE TABLE OF ddshretval,      "Interface Structure Search Help

    ls_return_values LIKE LINE OF lt_return_values,

    lv_rc            TYPE sysubrc.                  "Return Value of ABAP Statements

 

  FIELD-SYMBOLS <interface> TYPE ddshiface.        "Interface description of a F4 help method

 

*---------- Get search help ----------*

  CALL FUNCTION 'F4IF_GET_SHLP_DESCR'

    EXPORTING

      shlpname = 'SCTSOBJECT'

    IMPORTING

      shlp     = lt_shlp.

 

*---------- Fill search help ----------*

  LOOP AT lt_shlp-interface ASSIGNING <interface>.

    IF <interface>-shlpfield = 'OBJECT'.

      <interface>-valfield = abap_true.

      <interface>-value    = p_object.

    ENDIF.

    IF <interface>-shlpfield = 'PGMID'.

      <interface>-valfield = abap_true.

      <interface>-value    = p_pgmid.

    ENDIF.

  ENDLOOP.

 

*---------- Call search help ----------*

  CALL FUNCTION 'F4IF_START_VALUE_REQUEST'

    EXPORTING

      shlp          = lt_shlp

    IMPORTING

      rc            = lv_rc

    TABLES

      return_values = lt_return_values.

 

*---------- Set search help return ----------*

  IF lv_rc IS INITIAL.

    READ TABLE lt_return_values INTO ls_return_values WITH KEY fieldname = 'OBJECT'.

    IF sy-subrc IS INITIAL.

      p_object = ls_return_values-fieldval.

    ENDIF.

 

    READ TABLE lt_return_values INTO ls_return_values WITH KEY fieldname = 'PGMID'.

    IF sy-subrc IS INITIAL.

      p_pgmid = ls_return_values-fieldval.

    ENDIF.

  ENDIF.

 

ENDFORM.                                                    " OBJECT_F4

 

*----------------------------------------------------------------------*

* Form OBJECT_NAME_F4 - Object Name F4

*----------------------------------------------------------------------*

FORM object_name_f4.

 

  DATA lv_object_type TYPE seu_obj.  "Object type

 

*---------- Get objects repository information ----------*

  lv_object_type = p_object.

  CALL FUNCTION 'REPOSITORY_INFO_SYSTEM_F4'                 "#EC FB_RC

    EXPORTING

      object_type          = lv_object_type

      object_name          = p_obj_n

    IMPORTING

      object_name_selected = p_obj_n

    EXCEPTIONS

      cancel               = 1

      wrong_type           = 2

      OTHERS               = 3.

 

ENDFORM.                    " OBJECT_NAME_F4

 

*&---------------------------------------------------------------------*

*&      Form  RFC_F4

*&---------------------------------------------------------------------*

FORM rfc_f4 .

 

  CALL FUNCTION 'TMS_UI_F4_SYSTEMS'

    CHANGING

      cv_system = p_rfc_d.

 

ENDFORM.                    " RFC_F4

 

*&---------------------------------------------------------------------*

*&      Form  SCREEN_PAI

*&---------------------------------------------------------------------*

FORM screen_pai .

 

  DATA ls_env_dummy TYPE senvi.

 

  IF sy-ucomm = 'ONLI'.

*---------- Check required data ----------*

    IF p_pgmid IS INITIAL OR p_object IS INITIAL OR p_obj_n IS INITIAL OR

       ( p_rfc IS NOT INITIAL AND p_rfc_d IS INITIAL ).

      MESSAGE e001 WITH text-m02 space space space DISPLAY LIKE 'W'.  "Please fill all required fields

    ENDIF.

 

    IF p_rfc IS NOT INITIAL AND p_rfc_d = sy-sysid.

      MESSAGE e001 WITH text-m08 space space space DISPLAY LIKE 'W'.  "Please select a remote system

    ENDIF.

 

*---------- Add first object ----------*

    PERFORM progress_bar USING text-p01 '10'. "Adding object

 

    PERFORM check_add_object USING p_pgmid p_object p_obj_n ls_env_dummy.

    IF gt_objects IS INITIAL.

      MESSAGE e001 WITH text-m01 space space space DISPLAY LIKE 'W'.  "Object not found

    ENDIF.

  ENDIF.

 

ENDFORM.                    " SCREEN_PAI

 

*----------------------------------------------------------------------*

* Form PROGRESS_BAR

*----------------------------------------------------------------------*

FORM progress_bar USING i_value  TYPE itex132

                        i_tabix  TYPE i.

  DATA:

    lv_text(40),

    lv_percentage TYPE p,

    lv_percent_char(3).

 

  lv_percentage = ( i_tabix / 100 ) * 100.

  lv_percent_char = lv_percentage.

 

  SHIFT lv_percent_char LEFT DELETING LEADING ' '.

  CONCATENATE i_value '...' INTO i_value.

  CONCATENATE i_value lv_percent_char text-pb1 INTO lv_text SEPARATED BY space.

 

  IF lv_percentage GT gv_percent OR i_tabix = 1.

    CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'

      EXPORTING

        percentage = lv_percentage

        text       = lv_text.

 

    gv_percent = lv_percentage.

  ENDIF.

 

ENDFORM.                    " PROGRESS_BAR

 

*&---------------------------------------------------------------------*

*&      Form  RUN_CHECKS

*&---------------------------------------------------------------------*

FORM run_checks .

 

  IF gt_objects IS NOT INITIAL.

*---------- Dependecies check ----------*

    PERFORM progress_bar USING text-p02 '20'. "Checking Dependecies

    PERFORM objects_dependencies_check.

 

    PERFORM progress_bar USING text-p05 '50'. "Processing options

 

*---------- Include Tables Maintenance ----------*

    IF p_itm IS NOT INITIAL.

      PERFORM include_maintenances.

      PERFORM objects_dependencies_check.

    ENDIF.

 

*---------- Include Lock objects ----------*

    IF p_ilo IS NOT INITIAL. PERFORM include_locks. ENDIF.

 

*---------- Include Change documents ----------*

    IF p_icd IS NOT INITIAL. PERFORM include_cds. ENDIF.

 

*---------- Remote check ----------*

    IF p_rfc IS NOT INITIAL.

      PERFORM progress_bar USING text-p03 '80'. "Checking Remote

      PERFORM remote_objects_check.

    ENDIF.

  ENDIF.

 

ENDFORM.                    " RUN_CHECKS

 

*----------------------------------------------------------------------*

* Form DISPLAY_OBJECTS

*----------------------------------------------------------------------*

FORM display_objects.

 

  DATA:

    lr_events        TYPE REF TO cl_salv_events_table,      "ALV Events

    lr_display       TYPE REF TO cl_salv_display_settings,  "ALV Output Appearance

    lr_columns       TYPE REF TO cl_salv_columns_table,     "ALV Columns

    lr_column        TYPE REF TO cl_salv_column_table,

    lr_selections    TYPE REF TO cl_salv_selections,        "ALV Selections

    lr_layout        TYPE REF TO cl_salv_layout,            "ALV Layout

    lo_event_handler TYPE REF TO lcl_handle_events.         "ALV Events Handler

 

  DATA:

    lt_column_ref TYPE salv_t_column_ref,                   "Columns of ALV List

    ls_column_ref TYPE salv_s_column_ref,

    ls_key        TYPE salv_s_layout_key.

 

  DATA:

    lv_title   TYPE lvc_title,                              "ALV title

    lv_lines   TYPE i,                                      "Number of objects

    lv_lines_c TYPE string.

 

  IF gt_objects IS NOT INITIAL.

    PERFORM progress_bar USING text-p04 '90'. "Display objects

 

    IF go_objects IS NOT BOUND. "Create ALV

      TRY.

 

          IF lines( gt_objects ) = 1. "No dependecies found

            MESSAGE s001 WITH text-m14 space space space DISPLAY LIKE 'W'.

          ELSE.

            SORT gt_objects BY pgmid object obj_name.

          ENDIF.

 

*---------- Create ALV ----------*

          cl_salv_table=>factory( IMPORTING r_salv_table = go_objects

                                   CHANGING t_table      = gt_objects ).

 

*---------- Set ALV Functions ----------*

          go_objects->set_screen_status(

            pfstatus      = 'STATUS'

            report        = sy-cprog

            set_functions = go_objects->c_functions_all ).

 

*---------- Set Layout ----------*

          lr_layout = go_objects->get_layout( ).

          ls_key-report = sy-repid.

          lr_layout->set_key( ls_key ).

          lr_layout->set_save_restriction( ).

 

*---------- Set ALV selections ----------*

          lr_selections = go_objects->get_selections( ).

          lr_selections->set_selection_mode( if_salv_c_selection_mode=>row_column ).

 

*---------- Set ALV Display and Title ----------*

          lr_display = go_objects->get_display_settings( ).

          lr_display->set_striped_pattern( if_salv_c_bool_sap=>true ).

 

          lv_lines = lines( gt_objects ).

          lv_lines_c = lv_lines.

          CONDENSE lv_lines_c NO-GAPS.

          CONCATENATE '(' lv_lines_c ')' INTO lv_lines_c.

          CONCATENATE sy-title lv_lines_c INTO lv_title SEPARATED BY space.

          lr_display->set_list_header( lv_title ).

 

*---------- Set ALV Columns ----------*

          lr_columns = go_objects->get_columns( ).

          lr_columns->set_key_fixation( ).

          lr_columns->set_optimize( ).

          lt_column_ref = lr_columns->get( ).

 

          LOOP AT lt_column_ref INTO ls_column_ref. "Default format for all columns

            lr_column ?= lr_columns->get_column( ls_column_ref-columnname ).

            lr_column->set_f4( if_salv_c_bool_sap=>false ).

            lr_column->set_alignment( if_salv_c_alignment=>centered ).

 

            IF ls_column_ref-columnname = 'STATUS' OR

               ls_column_ref-columnname = 'PGMID' OR

               ls_column_ref-columnname = 'OBJECT' OR

               ls_column_ref-columnname = 'OBJ_NAME'.

              lr_column->set_key( if_salv_c_bool_sap=>true ).

            ENDIF.

 

            IF ls_column_ref-columnname = 'OBJ_NAME' OR

               ls_column_ref-columnname = 'DEVCLASS'.

              lr_column->set_alignment( if_salv_c_alignment=>left ).

            ENDIF.

 

            IF ls_column_ref-columnname = 'OBJ_DESC'.

              lr_column->set_alignment( if_salv_c_alignment=>left ).

              lr_column->set_short_text( text-c02 ).

              lr_column->set_medium_text( text-c02 ).

              lr_column->set_long_text( text-c02 ).

            ENDIF.

 

            IF ls_column_ref-columnname = 'REMOTE'.

              IF p_rfc IS INITIAL.

                lr_column->set_visible( if_salv_c_bool_sap=>false ).

              ELSE.

                lr_column->set_short_text( text-c01 ).

                lr_column->set_medium_text( text-c01 ).

                lr_column->set_long_text( text-c01 ).

              ENDIF.

            ENDIF.

          ENDLOOP.

 

*---------- Register ALV Events ----------*

          lr_events = go_objects->get_event( ).

          CREATE OBJECT lo_event_handler.

          SET HANDLER lo_event_handler->on_user_command FOR lr_events.

          SET HANDLER lo_event_handler->on_double_click FOR lr_events.

 

*---------- Display Objects ALV ----------*

          go_objects->display( ).

 

        CATCH cx_root.                                   "#EC CATCH_ALL

          MESSAGE s001 WITH text-m03 space space space DISPLAY LIKE 'E'.

      ENDTRY.

 

    ELSE. "Refresh ALV

      go_objects->refresh( ).

    ENDIF.

  ENDIF.

 

ENDFORM.                    " DISPLAY_OBJECTS

 

*----------------------------------------------------------------------*

* FORMS Adds

*----------------------------------------------------------------------*

 

*----------------------------------------------------------------------*

* Form CHECK_ADD_OBJECT

*----------------------------------------------------------------------*

FORM check_add_object USING value(i_pgmid) TYPE pgmid

                                  i_object TYPE any

                                   i_obj_n TYPE any

                                is_env_tab TYPE senvi.

 

  DATA lo_wb_object TYPE REF TO cl_wb_object.  "Repository Object

 

  DATA:

    ls_tadir          TYPE tadir,               "Directory of Repository Objects

    ls_wb_object_type TYPE wbobjtype,           "Global WB Type

    ls_object         LIKE LINE OF gt_objects.  "Objects to transport line

 

  DATA:

    lv_tr_object   TYPE trobjtype,  "Object Type

    lv_tr_obj_name TYPE trobj_name, "Object Name in Object List

    lv_trans_pgmid TYPE pgmid.      "Program ID in Requests and Tasks

 

*-------------------------------------------------- Object convertions *

  IF i_pgmid <> gc_r3tr.

    SELECT pgmid UP TO 1 ROWS FROM tadir                "#EC CI_GENBUFF

      INTO i_pgmid

     WHERE object   = i_object

       AND obj_name = i_obj_n.

    ENDSELECT.

 

*---------- Is not a TADIR object and Conversion required ----------*

    IF sy-subrc IS NOT INITIAL.

      lv_tr_object   = i_object.

      lv_tr_obj_name = i_obj_n.

 

      cl_wb_object=>create_from_transport_key( EXPORTING p_object                = lv_tr_object

                                                         p_obj_name              = lv_tr_obj_name

                                               RECEIVING p_wb_object             = lo_wb_object

                                              EXCEPTIONS objecttype_not_existing = 1

                                                         empty_object_key        = 2

                                                         key_not_available       = 3

                                                         OTHERS                  = 4 ).

      IF sy-subrc IS INITIAL.

        lo_wb_object->get_global_wb_key( IMPORTING p_object_type     = ls_wb_object_type

                                        EXCEPTIONS key_not_available = 1

                                                   OTHERS            = 2 ).

        IF sy-subrc IS INITIAL.

          lo_wb_object->get_transport_key( IMPORTING p_pgmid           = lv_trans_pgmid "#EC CI_SUBRC

                                          EXCEPTIONS key_not_available = 1

                                                     OTHERS            = 2 ).

*---------- Check Program ID ----------*

          CASE lv_trans_pgmid.

            WHEN gc_r3tr.  "Main objects

              i_pgmid = lv_trans_pgmid.

 

            WHEN 'LIMU'.  "Sub object

              CALL FUNCTION 'GET_R3TR_OBJECT_FROM_LIMU_OBJ'

                EXPORTING

                  p_limu_objtype = lv_tr_object

                  p_limu_objname = lv_tr_obj_name

                IMPORTING

                  p_r3tr_objtype = lv_tr_object

                  p_r3tr_objname = lv_tr_obj_name

                EXCEPTIONS

                  no_mapping     = 1

                  OTHERS         = 2.

 

              IF sy-subrc IS INITIAL.

                ls_object-pgmid    = gc_r3tr.

                ls_object-object   = lv_tr_object.

                ls_object-obj_name = lv_tr_obj_name.

                PERFORM add_object USING ls_object.

                RETURN.

              ENDIF.

 

            WHEN OTHERS.  "Include objects

              i_pgmid = gc_r3tr.

              CALL FUNCTION 'GET_TADIR_TYPE_FROM_WB_TYPE'

                EXPORTING

                  wb_objtype        = ls_wb_object_type-subtype_wb

                IMPORTING

                  transport_objtype = lv_tr_object

                EXCEPTIONS

                  no_mapping_found  = 1

                  no_unique_mapping = 2

                  OTHERS            = 3.

 

              IF sy-subrc IS INITIAL.

                i_object = lv_tr_object.

 

                IF is_env_tab-encl_obj IS NOT INITIAL.

                  i_obj_n = is_env_tab-encl_obj.

                ENDIF.

              ENDIF.

          ENDCASE.

 

        ENDIF.

      ENDIF.

 

    ENDIF.

  ENDIF.

 

*------------------------------------------------------ Check in TADIR *

  SELECT SINGLE * FROM tadir

    INTO ls_tadir

   WHERE pgmid    = i_pgmid

     AND object   = i_object

     AND obj_name = i_obj_n.

 

*---------------------------------------------------------- Add object *

  IF ls_tadir IS NOT INITIAL.

    MOVE-CORRESPONDING ls_tadir TO ls_object.

 

*---------- Set SAP Generated object status ----------*

    IF ls_tadir-genflag IS NOT INITIAL.

      ls_object-status = icon_led_yellow.

    ENDIF.

 

*---------- Add object to be checked ----------*

    PERFORM add_object USING ls_object.

 

*---------- Error Object not valid ----------*

  ELSE.

    IF lines( gt_objects ) > 0. "Skip first object

      ls_object-pgmid    = i_pgmid.

      ls_object-object   = i_object.

      ls_object-obj_name = i_obj_n.

      ls_object-status   = icon_led_red.

      PERFORM add_object USING ls_object.

    ENDIF.

  ENDIF.

 

ENDFORM.                    " CHECK_ADD_OBJECT

 

*&---------------------------------------------------------------------*

*&      Form  ADD_OBJECT_HEADER

*&---------------------------------------------------------------------*

FORM add_object_header USING i_pgmid  TYPE pgmid

                             i_object TYPE any

                             i_obj_n  TYPE any

                             i_objfunc TYPE objfunc.

 

  DATA ls_e071 LIKE LINE OF gt_e071.

 

  READ TABLE gt_e071 TRANSPORTING NO FIELDS WITH KEY pgmid    = i_pgmid

                                                     object   = i_object

                                                     obj_name = i_obj_n

                                                     objfunc  = i_objfunc.

  IF sy-subrc IS NOT INITIAL.

    ls_e071-pgmid    = i_pgmid.

    ls_e071-object   = i_object.

    ls_e071-obj_name = i_obj_n.

    ls_e071-objfunc  = i_objfunc.

    APPEND ls_e071 TO gt_e071.

  ENDIF.

 

ENDFORM.                    " ADD_OBJECT_HEADER

 

*&---------------------------------------------------------------------*

*&      Form  ADD_OBJECT_KEYS

*&---------------------------------------------------------------------*

FORM add_object_keys USING i_pgmid  TYPE pgmid

                           i_object TYPE any

                           i_obj_n  TYPE any

                           i_tabkey TYPE any.

 

  DATA ls_e071k LIKE LINE OF gt_e071k.

 

  ls_e071k-pgmid      = i_pgmid.

  ls_e071k-object     = i_object.

  ls_e071k-objname    = i_obj_n.

  ls_e071k-mastertype = i_object.

  ls_e071k-mastername = i_obj_n.

  ls_e071k-tabkey     = i_tabkey.

  APPEND ls_e071k TO gt_e071k.

 

ENDFORM.                    " ADD_OBJECT_KEYS

 

*&---------------------------------------------------------------------*

*&      Form  ADD_OBJECT

*&---------------------------------------------------------------------*

FORM add_object USING ps_object TYPE gty_objects.

 

  DATA:

    ls_objs_desc LIKE LINE OF gt_objs_desc,  "Objects prograns ID line"Info Environment

    lt_devclass  TYPE scts_devclass,         "Development Packages

    ls_devclass  TYPE trdevclass.

 

  DATA:

    lv_object    TYPE trobjtype,  "Object Type

    lv_objname   TYPE sobj_name,  "Object Name in Object Directory

    lv_namespace TYPE namespace.  "Object Namespace

 

*---------- Check if already added ----------*

  READ TABLE gt_objects TRANSPORTING NO FIELDS WITH KEY pgmid    = ps_object-pgmid

                                                        object   = ps_object-object

                                                        obj_name = ps_object-obj_name.

  IF sy-subrc IS NOT INITIAL. "New object

*---------------------------------------- Check if is customer objects *

    lv_object  = ps_object-object.

    lv_objname = ps_object-obj_name.

    CALL FUNCTION 'TRINT_GET_NAMESPACE'                     "#EC FB_RC

      EXPORTING

        iv_pgmid            = ps_object-pgmid

        iv_object           = lv_object

        iv_obj_name         = lv_objname

      IMPORTING

        ev_namespace        = lv_namespace

      EXCEPTIONS

        invalid_prefix      = 1

        invalid_object_type = 2

        OTHERS              = 3.

 

    IF lv_namespace = '/0CUST/'.  "Is customer object

 

*---------- Read object description ----------*

      READ TABLE gt_objs_desc INTO ls_objs_desc WITH KEY object = ps_object-object.

      IF sy-subrc IS INITIAL.

        ps_object-obj_desc = ls_objs_desc-text.  "Object type description

      ENDIF.

 

*---------- Read development class tecnical information ----------*

      IF ps_object-devclass IS INITIAL.

        SELECT SINGLE devclass FROM tadir

          INTO ps_object-devclass

         WHERE pgmid    = ps_object-pgmid

           AND object   = ps_object-object

           AND obj_name = ps_object-obj_name.

      ENDIF.

 

      IF ps_object-devclass IS NOT INITIAL AND ps_object-devclass <> gc_temp.

        ls_devclass-devclass = ps_object-devclass.

        APPEND ls_devclass TO lt_devclass.

 

        CALL FUNCTION 'TR_READ_DEVCLASSES'

          EXPORTING

            it_devclass = lt_devclass

          IMPORTING

            et_devclass = lt_devclass.

 

        READ TABLE lt_devclass INTO ls_devclass INDEX 1.

        IF sy-subrc IS INITIAL.

          ps_object-target = ls_devclass-target.  "Development package target

        ENDIF.

      ENDIF.

 

*---------- Add object to transport ----------*

      APPEND ps_object TO gt_objects.

    ENDIF.

  ENDIF.

 

ENDFORM.                    " ADD_OBJECT

 

*&---------------------------------------------------------------------*

*&      Form  ADD_TOBJ_CONTENT

*&---------------------------------------------------------------------*

FORM add_tobj_content USING p_obj_name TYPE sobj_name.

 

  CONSTANTS:

    lc_tabu  TYPE trobjtype VALUE 'TABU',

    lc_tvdir TYPE sobj_name VALUE 'TVDIR',

    lc_tddat TYPE sobj_name VALUE 'TDDAT',

    lc_tvimf TYPE sobj_name VALUE 'TVIMF'.

 

  DATA:

    lt_tvimf TYPE TABLE OF tvimf,   "User routines called from view maintenance

    ls_tvimf LIKE LINE OF lt_tvimf.

 

  DATA lv_tabkey TYPE tabkey.  "Table Key

 

*---------- Add table content ----------*

  PERFORM add_object_header USING gc_r3tr lc_tabu lc_tvdir 'K'.

  PERFORM add_object_keys   USING gc_r3tr lc_tabu lc_tvdir p_obj_name.

 

  CLEAR lt_tvimf. "Read User routines called from view maintenance

  SELECT * FROM tvimf                                   "#EC CI_GENBUFF

    INTO TABLE lt_tvimf

   WHERE tabname = p_obj_name.

 

  LOOP AT lt_tvimf INTO ls_tvimf.

    AT FIRST.

      PERFORM add_object_header USING gc_r3tr lc_tabu lc_tvimf 'K'.

    ENDAT.

    lv_tabkey    = p_obj_name.

    lv_tabkey+30 = ls_tvimf-event.

    PERFORM add_object_keys USING gc_r3tr lc_tabu lc_tvimf lv_tabkey.

    CLEAR lv_tabkey.

  ENDLOOP.

 

  PERFORM add_object_header USING gc_r3tr lc_tabu lc_tddat 'K'.

  PERFORM add_object_keys   USING gc_r3tr lc_tabu lc_tddat p_obj_name.

 

ENDFORM.                    " ADD_TOBJ_CONTENT

 

*----------------------------------------------------------------------*

* FORMS Checks

*----------------------------------------------------------------------*

 

*----------------------------------------------------------------------*

* Form OBJECTS_DEPENDENCIES_CHECK

*----------------------------------------------------------------------*

FORM objects_dependencies_check .

 

  DATA:

    lv_obj_type TYPE seu_obj,         "Object type

    lt_env_tab  TYPE TABLE OF senvi,  "Object to check dependencies

    ls_env_tab  TYPE senvi.           "Info Environment

 

  DATA lv_no_rfc TYPE abap_bool.

 

  FIELD-SYMBOLS <ls_object> LIKE LINE OF gt_objects.  "Objects to transport

 

  LOOP AT gt_objects ASSIGNING <ls_object> WHERE status IS INITIAL.

 

*---------- Exclude RFCs if exist ----------*

    IF p_erfc IS NOT INITIAL AND <ls_object>-object = gc_fugr.

      CLEAR lv_no_rfc.

      PERFORM exclude_rfcs USING <ls_object>-obj_name CHANGING lv_no_rfc.

      IF lv_no_rfc IS INITIAL.

        <ls_object>-status = icon_led_yellow.

        CONTINUE.

      ENDIF.

    ENDIF.

 

*---------- Get object dependecies ----------*

    REFRESH lt_env_tab.

    lv_obj_type = <ls_object>-object.

 

    CALL FUNCTION 'REPOSITORY_ENVIRONMENT_RFC'

      EXPORTING

        obj_type        = lv_obj_type

        object_name     = <ls_object>-obj_name

      TABLES

        environment_tab = lt_env_tab.

 

    IF lines( lt_env_tab ) IS INITIAL AND lines( gt_objects ) = 1.  "Object dependencies check not support

      MESSAGE s001 WITH text-m13 space space space DISPLAY LIKE 'E'.

      <ls_object>-status = icon_led_red.

 

    ELSE.

      DELETE lt_env_tab INDEX 1.  "Delete first line

 

*---------- Add founded dependecies ----------*

      LOOP AT lt_env_tab INTO ls_env_tab.                "#EC CI_NESTED

        PERFORM check_add_object USING space ls_env_tab-type ls_env_tab-object ls_env_tab.

      ENDLOOP.

 

      <ls_object>-status = icon_led_green.  "Status checked

    ENDIF.

  ENDLOOP.

 

ENDFORM.                    " OBJECTS_DEPENDENCIES_CHECK

 

*----------------------------------------------------------------------*

* Form REMOTE_OBJECTS_CHECK

*----------------------------------------------------------------------*

FORM remote_objects_check .

 

  FIELD-SYMBOLS <ls_object> LIKE LINE OF gt_objects. "Objects to transport

 

  DATA:

    ls_e071 TYPE e071,          "Change & Transport System: Object Entries of Requests/Tasks

    lt_vrso TYPE TABLE OF vrso, "Version control: Object list (subset of VRSD)

    ls_vrso LIKE LINE OF lt_vrso.

 

  DATA:

    lv_diagnosis       TYPE char20, "Version check result

    lv_local_rfc_dest  TYPE rfcdest,  "Logical Destination

    lv_remote_rfc_dest TYPE rfcdest.

 

*---------- Get local rfc destination ----------*

  SELECT desadm FROM tmscsys UP TO 1 ROWS               "#EC CI_NOFIRST

    INTO lv_local_rfc_dest

   WHERE sysnam = sy-sysid.

  ENDSELECT.

 

*---------- Get remote rfc destination ----------*

  SELECT desadm FROM tmscsys UP TO 1 ROWS               "#EC CI_NOFIRST

    INTO lv_remote_rfc_dest

   WHERE sysnam = p_rfc_d.

  ENDSELECT.

 

*---------- Check objects versions ----------*

  IF lv_local_rfc_dest IS NOT INITIAL AND lv_remote_rfc_dest IS NOT INITIAL.

    LOOP AT gt_objects ASSIGNING <ls_object>.

 

*---------- Get Sub Objects ----------*

      ls_e071-object   = <ls_object>-object.

      ls_e071-obj_name = <ls_object>-obj_name.

 

      REFRESH lt_vrso.

      CALL FUNCTION 'TRINT_RESOLVE_OBJ'

        EXPORTING

          is_e071             = ls_e071

        TABLES

          et_vrso             = lt_vrso

        EXCEPTIONS

          not_versionable     = 1

          communication_error = 2

          OTHERS              = 3.

 

      IF sy-subrc IS INITIAL.

*---------- Remote check all objects and subobjects ----------*

        LOOP AT lt_vrso INTO ls_vrso WHERE objtype <> 'DOCU'. "#EC CI_NESTED

 

          CLEAR lv_diagnosis.

          CALL FUNCTION 'TRINT_COMP_VERSION'

            EXPORTING

              is_vrso          = ls_vrso

              dest1            = lv_local_rfc_dest

              dest2            = lv_remote_rfc_dest

            IMPORTING

              ev_diagnosis     = lv_diagnosis

            EXCEPTIONS

              rfc_error_loc    = 1

              rfc_error_rem    = 2

              intern_error_loc = 3

              intern_error_rem = 4

              OTHERS           = 5.

 

          IF sy-subrc IS INITIAL.

            CASE lv_diagnosis.

              WHEN 0. "Equal

                <ls_object>-remote = text-o01.

              WHEN 1. "Different

                <ls_object>-remote = text-o02.

                EXIT.

              WHEN 3. "New

                <ls_object>-remote = text-o03.

                EXIT.

            ENDCASE.

          ENDIF.

        ENDLOOP.

 

      ELSE. "Not versionable

        <ls_object>-remote = text-o04.

      ENDIF.

    ENDLOOP.

 

  ELSE. "RFCs destinations missing

    MESSAGE s001 WITH text-m09 space space space DISPLAY LIKE 'W'.

  ENDIF.

 

ENDFORM.                    " REMOTE_OBJECTS_CHECK

 

*&---------------------------------------------------------------------*

*&      Form  INCLUDE_MAINTENANCES

*&---------------------------------------------------------------------*

FORM include_maintenances .

 

  DATA:

    ls_tvdir      TYPE tvdir,               "View Directory

    ls_object     LIKE LINE OF gt_objects,  "Objects to transport line

    ls_object_add LIKE LINE OF gt_objects.

 

  DATA:

    lv_obj_type   TYPE c,

    lv_tobj_name  TYPE sobj_name, "Object Name in Object Directory

    lv_objectname TYPE ob_object. "Object Name

 

  LOOP AT gt_objects INTO ls_object WHERE object = gc_tabl AND status = icon_led_green.

 

    CLEAR ls_tvdir.

    SELECT SINGLE * FROM tvdir                       "#EC CI_SEL_NESTED

      INTO ls_tvdir

     WHERE tabname = ls_object-obj_name.

 

    IF sy-subrc IS INITIAL.

*---------- Add Function Group if exist ----------*

      ls_object_add-pgmid    = gc_r3tr.

      ls_object_add-object   = gc_fugr.

      ls_object_add-obj_name = ls_tvdir-area.

      PERFORM add_object USING ls_object_add.

 

      IF ls_tvdir-bastab IS INITIAL. lv_obj_type = 'V'. ELSE. lv_obj_type = 'S'. ENDIF.

 

*---------- Add Definition of a Maintenance and Transport Object ----------*

      CLEAR lv_tobj_name.

      lv_objectname = ls_object-obj_name.

      CALL FUNCTION 'CTO_OBJECT_GET_TADIR_KEY'

        EXPORTING

          iv_objectname = lv_objectname

          iv_objecttype = lv_obj_type

        IMPORTING

          ev_obj_name   = lv_tobj_name.

 

      ls_object_add-pgmid    = gc_r3tr.

      ls_object_add-object   = gc_tobj.

      ls_object_add-obj_name = lv_tobj_name.

      ls_object_add-status   = icon_led_green.

      PERFORM add_object USING ls_object_add.

    ENDIF.

  ENDLOOP.

 

ENDFORM.                    " INCLUDE_MAINTENANCES

 

*&---------------------------------------------------------------------*

*&      Form  INCLUDE_LOCKS

*&---------------------------------------------------------------------*

FORM include_locks .

 

  DATA:

    ls_object     LIKE LINE OF gt_objects,  "Objects to transport line

    ls_object_add LIKE LINE OF gt_objects.

 

  DATA lv_viewname TYPE viewname.

 

*---------- Add lock objects if exist ----------*

  LOOP AT gt_objects INTO ls_object WHERE object = gc_tabl AND status = icon_led_green.

 

    CLEAR lv_viewname.

    SELECT viewname FROM dd25l UP TO 1 ROWS "#EC CI_SEL_NESTED "#EC CI_NOFIRST

      INTO lv_viewname

     WHERE aggtype = 'E'

       AND roottab = ls_object-obj_name.

    ENDSELECT.

 

    IF sy-subrc IS INITIAL.

      ls_object_add-pgmid    = gc_r3tr.

      ls_object_add-object   = 'ENQU'.

      ls_object_add-obj_name = lv_viewname.

      ls_object_add-status   = icon_led_green.

      PERFORM add_object USING ls_object_add.

    ENDIF.

  ENDLOOP.

 

ENDFORM.                    " INCLUDE_LOCKS

 

*&---------------------------------------------------------------------*

*&      Form  INCLUDE_CDS

*&---------------------------------------------------------------------*

FORM include_cds .

 

  DATA:

    ls_object     LIKE LINE OF gt_objects,  "Objects to transport line

    ls_object_add LIKE LINE OF gt_objects.

 

  DATA lv_object TYPE cdobjectcl.

 

*---------- Add change document object if exist ----------*

  LOOP AT gt_objects INTO ls_object WHERE object = gc_tabl AND status = icon_led_green.

 

    CLEAR lv_object.

    SELECT object FROM tcdob UP TO 1 ROWS "#EC CI_SEL_NESTED "#EC CI_GENBUFF

      INTO lv_object

     WHERE tabname = ls_object-obj_name.

    ENDSELECT.

 

    IF sy-subrc IS INITIAL.

      ls_object_add-pgmid    = gc_r3tr.

      ls_object_add-object   = gc_chdo.

      ls_object_add-obj_name = lv_object.

      ls_object_add-status   = icon_led_green.

      PERFORM add_object USING ls_object_add.

    ENDIF.

  ENDLOOP.

 

ENDFORM.                    " INCLUDE_CDS

 

*&---------------------------------------------------------------------*

*&      Form  EXCLUDE_RFCS

*&---------------------------------------------------------------------*

FORM exclude_rfcs USING p_obj_name TYPE sobj_name

               CHANGING p_no_rfc   TYPE abap_bool.

 

  DATA lt_fbinfo_remote TYPE TABLE OF fbinfor.  "Function Module Information

  DATA lv_complete_area TYPE rs38l_area.        "Function group, to which the function module belongs

 

*---------- Check if all are RFCs ----------*

  lv_complete_area = p_obj_name.

  CALL FUNCTION 'FUNCTION_SELECT_TFDIR'                     "#EC FB_RC

    EXPORTING

      im_complete_area        = lv_complete_area

    IMPORTING

      ex_fbinfo_remote        = lt_fbinfo_remote

    EXCEPTIONS

      include_not_found_trdir = 1

      report_source_not_found = 2

      permission_failure      = 3

      OTHERS                  = 4.

 

  LOOP AT lt_fbinfo_remote TRANSPORTING NO FIELDS WHERE remote <> 'R'.

    p_no_rfc = abap_true. "One function found that are not RFCs

    EXIT.

  ENDLOOP.

 

ENDFORM.                    " EXCLUDE_RFCS


Selection screen layout after implementation:

ex4.png

Result objects list layout:

ex8.png

 

Please consider before using:

  • This tool is not full tested and is not possible to ensure that all repository workbench objects can be checked or dependencies detected. Please give feedback if you notice missing objects to be improved.
  • Only objects inside customer scope /0CUST/ are detected and checked. All standard objects are excluded because they are not relevant for transport and performance is significantly improved.
  • ZNM_OBJS_DEP_TRANS works with most common and used objects. Exotic objects like Sapscript, Smart forms, IDOCs, Workflows and others for the moment are not fully supported.
  • For complex and big developments you should use it as an auxiliary tool and kept in mind that your brain is still needed to validate and create coherent transports.
  • You should have some technical background and ABAP knowledge to use and understand how to take full advantage of it.
  • Dependencies result objects list only includes main objects to be transported. This mean that for example if a function module and/or class method are detected the full function group and class are included and not their sub-objects. The same for special SAP code generated objects, only main objects are included because they are generated at import step and should not be transported.
  • You can only include in one transport request objects with green status with a valid development package and belonging to the same target. Objects with yellow status are ignored and not relevant but for red status a manual check is required to avoid transport problems.
  • Development packages and tables data are not displayed on dependencies result list and are added directly to transport request.

 

How to use:

  • Fill object selection using main objects like Tables, Programs, Classes, Functions Groups, etc to be checked.

Example to check dependencies in program ZNM_OBJS_DEP_TRANS:

          ex1.png

  • If you need to detected objects changes in destination system please flag Cross-system objects version check:

          ex2.png

  • Flag additional options if needed to include or exclude specials objects:

          ex9.png

    • Include Tables Maintenances: If exist in detected tables function groups and related table maintenance objects are included;
    • Include Tables Locks: If exist in detected tables lock objects are included;
    • Include Tables CDs: If exist in detected tables change documents are included;
    • Exclude RFCs if exist: Depending of your SAP systems landscape this option can be useful to exclude RFCs functions modules.

 

  • Double click on each result row to direct object editor navigation.

 

Nuno Morais's Blog

 

Nuno Morais | LinkedIn

My Tips about how to handle complex and tricky issues

$
0
0

Symptoms of complex & tricky issues

 

During my seven years working on SAP China, I have resolved hundreds of internal tickets or customer tickets. Among them there are some kinds of tickets which make me headache:

 

 

1. The issue needs complex steps to reproduce

  For example I have ever resolved one customer ticket, I need to (1) create a new sales order (2) create a new customer demand based on sales order (3) create a pick list (3) release the generated delivery note. The issue can only be reproduced by releasing the delivery note. Then I have to repeat the lengthy steps (1) ~ (3) and do debugging in note release.

 

2. Different software components involved

  I bet most of you guys have such feeling: if the issue is purely occuring within your responsibe component, you will always be confident that it could be resolved sooner or later, since you are the owner of your API and quite familar with it. However if your API is called by other software component or from other system with complex context, you have to spend more time to have a basic understanding of the whole story, to find how your API is called, to analyze whether your original design of API could sustain this new challenge you never think about before?

 

3. The issue could only be reproduced in customer production system

  In most of the cases I ever meet, the reason is because of the data setup. For example in customer test system, the test data is not well set up so that the errorous code has no chance to be executed in test system. Sometimes there is technical limitation or whatever other reasons so that it is impossible for you to ask customers to setup exactly the same data in test system as the data they are using in production system. The worst situation is, sometimes the issue occurs during write operation, for example the pricing calculation is wrong when a business document is saved. In this case you can not simply debug the save process, as it will influence customer business. You have to coordinate with customer how to proceed.

 

4. The issue could only be reproduced in background job execution mode but not in online mode

  The first step to check such issue is trying to find whether there are some FMs or methods which should not be used during background execution when the presentation server is not attached.

 

5. The issue could only be reproduced in normal execution, but when you debug the program, everything works perfectly

  Everyday I use debugger to fight against bug. When I found the bug could not be found via debugging, however it does exist in fact, I feel helpless, since this powerful weapean could not help me out this time. Then I have to read and analyze the code, and make them running in my brain. In most cases finally the issue is related to time-dependent processing in the program.

 

  As an ABAPer we are lucky since we do not always have to struggle with such time-dependent issues. When I am developing an Android application for SAP CRM customer briefing in year 2012, I suffer a lot from such kinds of issues. Just two examples:

 

  a. When you touch the Android tablet with single finger and make a slip, there are 5 or 6 different kinds of events triggered sequentially. My event handler registered for these events will handle with the coordinates of events occurred. Those coordinates will become invalid if code stopped in debugger. Then I have to write many System.out.println to print the coordinate in console for analysis.

 

  b. Dead lock in multi-threading. Such issue is hard to reproduce via debugging.

 

In fact some issue does not simply fall into one or two categories listed above but consists of several of them. I never encounter an issue from customer which contains all the five feature above, and I pray I will NEVER meet with it.

 

An example of how to resolve such kind of issue

 

Recently I have been working on one ticket which took me totally almost 10 hours to resolve it. I will share how I analyze this issue step by step.

 

I am owner of SAP CRM IBASE component CRM-MD-INB, the issue is my Solution management development team colleague complains when they create a new IBASE component and delete it afterwards in the same session and do a save operation, there will be ST22 dump in middleware processing stuff.

 

clipboard1.png

I know nothing about solution management development before.

This issue could only be reproduced in background execution. ( The program is designed to only execute in background )

The issue is not always reproducible. 囧

 

clipboard2.png

Step1 Understand how and when my API is called

 

I quickly go through the solution manager program, there are tens of thousands code. I set breakpoint inside my API ( IBASE create, update and delete function module ), then identify all calling space and importing parameter content.

 

 

Step2 Write simulation report and ensure the issue could be reproduced via it

 

As the scenario is really complex - CRM, SOL and Middleware involved, I spent one hour debugging without any hint found. Purely judgement based on code level, there are too many factors which will impact the program. In order to make me concentrate on my API, I plan to develop a simulation report which also calls IBASE create, update and delete and then perform save. The idea is to make the API call decouple from the solution manager logic. If the issue could then also be reproduced in my simulation report, then life is eaiser - I then only have to work on the simulation report which only contains 200 lines.

 

I have spent another 1 hour to finish the simulation report. Unfortunately I cannot reproduce the issue with it. After I check again with issue reporter,

I realized that the report does not 100% simulate the real program regarding IBASE operation, and I change it to fix the gap.

The simulation report is uploaded as attachment of this blog.

 

clipboard3.png

 

Since the simulation report is owned by me, it is very convenient to change it for issue analysis.

 

a. comment out all IBASE related code.

b. uncomment IBASE component creation FM, and execute report - no dump

c. continue to uncomment IBASE component change FM, and execute - no dump

d. continue to uncomment IBASE component deletion FM, and execute - dumps!!!

 

So now I get to know this issue is related to IBASE deletion.

 

Step4 Investigation on ST22 dump

 

Now the issue could be reproduced during normal execution of simulation report, but could work perfectly well during debugging.

My previois experience told me that it might be caused by some time dependent processing logic in the code. Then I check the position of code which raises error( line 103 ) and found lots of time operation logic in the same include:

clipboard4.png

 

The aim of this include is to find the IBASE and filled it into es_ibinadm. First check in buffer table gt_ibinadm_by_in_guid, if failed then try FM in line 91( in the first screenshot of this blog) as last defence. In normal case, the es_ibinadm is expected to be filled however in this issue, the last defence also fails so the X message is raised.  I set breakpoint in this include, however during my debugging, the variable es_ibinadm is successfully filled in line 54, everything works pefectly. However the dump is indeed there when I execute the report directly.

 

So I run the report once again and go to ST22, this means the dump there is "fresh" and the Debugger button is available only in this fresh state, so that I can observe the variable value in debugger when the dump occurs.

 

clipboard5.png

I soon find the root cause: the valfr and valto of the buffer entry is the same,


clipboard6.png

so during normal execution, the check in line 53 fails, so the code has to try the last defence to call FM CRM_IBASE_COMP_GET_DETAIL. In this case, it is expected behavior to raise an X message since the entry in the buffer table should be returned. When the code is executed in debugger, the valto is always greater than valfr, so the code directly return the entry to its caller without further call on FM CRM_IBASE_COMP_GET_DETAIL.


clipboard7.png

I will not go deep into IBASE valfr and valto generation logic as it is CRM specific and I am also not expert on it. ( a default creation of IBASE component creation will set its valid to timestamp as a never invalid date( 99991231235959 ). The comparison timestamp is set as valid from timestamp )


clipboard8.png

After I add the following code to ensure the check in line 53 in above screenshot will always succeed, the issue is resolved - no dump in background job execution any more.

clipboard9.png


I guess it would also work if the "<" is changed to "<=" in line53. However this code is owned by Middleware software component and I could not change, maybe I can discuss with responsible colleague.


clipboard10.png


Summary

 

1. Benefit of simulation report

 

Although it took me 1 hour to develop the simulation report, I think it is definitely worth since it liberates me from spending lots of time and effort to debug the unfamiliar solution management program and enable me to concentrate on the core code which might be related to the dump.

Sometimes if you have some findings and need to make changes on the code which calls your API for verification, you can not really do this since the code is not owned by you. In this case the simulation report plays its role! You can change it at your will for verification.

 

2. The Mini-System methodology for issue-isolation

 

In early ten years of 21 Century, it is very popular in China to assemble a PC by ourselves via DIY approach. It means we buy CPU, memory chip, hard disk, motherboard and other stuffs from different hardware manufacturers and assemble them. Most common issue is after assembly, the computer cannot boot at all. Then we use "Mini-System" for trouble shooting: as first step we only try to boot computer with LEAST necessary hardwares ( CPU + Power + Motherboard: these three components constitute a so called "Mini-System" ). If the first attempt succeed, we can append additional component, but ensure only ONE new component is added in EACH step. Such iteration could enable us to find which hardware makes the boot failed.

 

clipboard11.png

Compared with computer system, our ABAP program is much more complex and issue-isolation is then necessary for root cause investigation.

In my issue processing I used "Mini-System" methodology to finally identify that the dump is related to the incorrect call of IBASE delete function module.

 

3. Try to gain a perspective of overall situation of the issue

 

In this issue processing I spent quite a lot of time to debug why function module CRM_IBASE_COMP_GET_DETAIL raises an X message in the beginning.

Inside this FM it calls some deeper APIs which are not owned by me so I waste lots of time to understand the logic. Later after I read the whole source code of includes where the CRM_IBASE_COMP_GET_DETAIL is called, I asked myself: should it be called at all? Why is it called in normal execution to get data from DB, although the entry is already available in the buffer??

 

Do not think solely, think holistically

 

It makes sense to spend time and effort to debug the code where the exception is raised to understand why. It makes MORE sense to investigate the code holistically, analyze the callstack and execution context of the code. If the code ( method or function module ) fails to generate the correct output as you expect, ask yourself:  should it be called at all?

 

Hope this blog can help for your issue analysis. And also welcome to share your tip & experience regarding tough issue processing


Dynamic ALV Tree from 1 to 5 levels - Only changing perform parameters

$
0
0

Hi All,

Here is my code to generate ALV tree with 5 hierarchy dynamically (or less according with your needs and with no change in the code)  and changing just a perform parameters to generate it. I don’t know if it have a easier and better way to do but this one is working

 

If you know another way please share

 

So follow the code:

 

Declarations:

  • My output table and work area are called, respectively: it_output and wa_output.
  • My fieldcat declaration fieldcat  TYPE lvc_t_fcat,

 

DATA: tree1              TYPE REF TO cl_gui_alv_tree.

DATA: v_icon1            TYPE lvc_s_layi,
     v_icon2           
TYPE lvc_s_layi,
     v_icon3           
TYPE lvc_s_layi,
     v_icon4           
TYPE lvc_s_layi,
     v_icon5           
TYPE lvc_s_layi.

FIELD-SYMBOLS: <field1>    TYPE any,
              <field2>   
TYPE any,
              <field3>   
TYPE any,
              <field4>   
TYPE any,
              <field5>   
TYPE any,
              <reffield1>
TYPE any,
              <reffield2>
TYPE any,
              <reffield3>
TYPE any,
              <reffield4>
TYPE any,
              <reffield5>
TYPE any.

 

 

Logic:

  • Hierarchic Field parameter must be called EXACTLY as the field name declarated in your output table. 
  • Ref tab and Ref Field will be used to bring the field information (data element). <reffieldX> will be used to compare the last information and the actual one and create a new node or keep the informationa agrrouped. It’s really important to have it done correctly! You can’t compare bananas to apples, right?!
  • If you want just 3 levels, clean the #4 and #5 in field_symbol. (Keep the '' though) Simply like that.

 

*                                                           Hierarchic Field / Ref Tab /  Ref Field
PERFORM f_field_symbol USING: 'TYPED'    'T6B1T'      'VTEXT' "1
                             
'DESC'      'TB038B'    'TEXT'  "2
                             
'KNUMA'    'KONA'       'KNUMA' "3
                             
'KSCHL'     'VAKEVB'  'KSCHL' "4
                             
'KOTABNR' 'VAKEVB'  'KOTABNR'. "5

*&---------------------------------------------------------------------*
*&      Form  F_FIELD_SYMBOL
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_0299   text
*      -->P_0300   text
*      -->P_0301   text
*----------------------------------------------------------------------*
FORM f_field_symbol  USING field1 table1 param1
                            field2 table2 param2
                            field3 table3 param3
                            field4 table4 param4
                            field5 table5 param5
.

DATA r_elemdescr  TYPE REF TO cl_abap_elemdescr.
DATA r_field      TYPE REF TO data.
DATA: data_element TYPE dd04d-rollname.

IF field1 IS NOT INITIAL.

ASSIGN COMPONENT field1 OF STRUCTURE wa_output TO <field1>.
CHECK sy-subrc = 0.

PERFORM f_fieldinfo USING table1 param1
CHANGING data_element.

r_elemdescr ?= cl_abap_elemdescr
=>describe_by_name( data_element ).
CREATE DATA r_field TYPE HANDLE r_elemdescr.
ASSIGN r_field->* TO <reffield1>.
ENDIF.

IF field2 IS NOT INITIAL.

ASSIGN COMPONENT field2 OF STRUCTURE wa_output TO <field2>.
CHECK sy-subrc = 0.

PERFORM f_fieldinfo USING table2 param2
CHANGING data_element.

r_elemdescr ?= cl_abap_elemdescr
=>describe_by_name( data_element ).
CREATE DATA r_field TYPE HANDLE r_elemdescr.
ASSIGN r_field->* TO <reffield2>.
ENDIF.

IF field3 IS NOT INITIAL.

ASSIGN COMPONENT field3 OF STRUCTURE wa_output TO <field3>.
CHECK sy-subrc = 0.

PERFORM f_fieldinfo USING table3 param3
CHANGING data_element.

r_elemdescr ?= cl_abap_elemdescr
=>describe_by_name( data_element ).
CREATE DATA r_field TYPE HANDLE r_elemdescr.
ASSIGN r_field->* TO <reffield3>.

ENDIF.

IF field4 IS NOT INITIAL.

ASSIGN COMPONENT field4 OF STRUCTURE wa_output TO <field4>.
CHECK sy-subrc = 0.

PERFORM f_fieldinfo USING table4 param4
CHANGING data_element.

r_elemdescr ?= cl_abap_elemdescr
=>describe_by_name( data_element ).
CREATE DATA r_field TYPE HANDLE r_elemdescr.
ASSIGN r_field->* TO <reffield4>.
ENDIF.

IF param5 IS NOT INITIAL.
ASSIGN COMPONENT field5 OF STRUCTURE wa_output TO <field5>.
CHECK sy-subrc = 0.

PERFORM f_fieldinfo USING table5 param5
CHANGING data_element.

r_elemdescr ?= cl_abap_elemdescr
=>describe_by_name( data_element ).
CREATE DATA r_field TYPE HANDLE r_elemdescr.
ASSIGN r_field->* TO <reffield5>.
ENDIF.

PERFORM build_sort_table USING field1 field2 field3 field4 field5.

ENDFORM.                    " F_FIELD_SYMBOL

*&---------------------------------------------------------------------*
*&      Form  F_FIELDINFO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_TABLE1  text
*      -->P_PARAM1  text
*      <--P_DATA_ELEMENT  text
*----------------------------------------------------------------------*
FORM f_fieldinfo  USING    table
param
CHANGING data_element.


DATA: BEGIN OF dfies OCCURS 100.
INCLUDE STRUCTURE dfies.
DATA: END OF dfies.


DATA: tablenm  TYPE ddobjname,
fieldnm 
TYPE dfies-fieldname.

MOVE table TO tablenm.
MOVE param TO fieldnm.

***  Fname Description

IF NOT fieldnm IS INITIAL.
CALL FUNCTION 'DDIF_FIELDINFO_GET'
EXPORTING
tabname       
= tablenm
fieldname     
= fieldnm
langu         
= sy-langu
TABLES
dfies_tab     
= dfies
EXCEPTIONS
not_found     
= 1
internal_error
= 2
OTHERS         = 3.
IF sy-subrc = 0.
READ TABLE dfies INDEX 1.
data_element
= dfies-rollname.
ENDIF.
ENDIF.

ENDFORM.                    " F_FIELDINFO


Creating hierarchies

  • Basically it will change according to what you used as parameter in perform f_field_symbol.
  • Each node have your own perform because if you want to fill the 2nd node with something different to the standard counts (sum, avg, max, min, ... – flagging H_FTYPE on fieldcat and using call method tree1->update_calculations.)  you need to use the same work are type used in your detailed line and organize it to show up what you want to.

 

*&---------------------------------------------------------------------*
*&      Form  CREATE_HIERARCHY
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*  -->  p1        text
*  <--  p2        text
*----------------------------------------------------------------------*
FORM create_hierarchy .

* add data to tree
DATA: l_last_key  TYPE lvc_nkey,
l_kotabnr  
TYPE vakevb-kotabnr,
l_knuma    
TYPE vakevb-knuma,
l_desc
(100) TYPE c,
l_kschl    
TYPE vakevb-kschl,
l_add      
TYPE c.

DATA: l_param_key  TYPE lvc_nkey,
l_param2_key
TYPE lvc_nkey,
l_param3_key
TYPE lvc_nkey,
l_param4_key
TYPE lvc_nkey,
l_param5_key
TYPE lvc_nkey.

LOOP AT it_output INTO wa_output.

**** LEVEL 1
IF <field1> IS ASSIGNED.
IF <field2> IS ASSIGNED. "Level 2 is empty
IF <field1> IS NOT INITIAL.
IF <reffield1> NE <field1>.
PERFORM level1 USING ''
v_icon1
-fieldname
CHANGING l_param_key.


IF <field2> IS ASSIGNED.
CLEAR: <reffield2>.
ENDIF.

IF <field3> IS ASSIGNED.
CLEAR: <reffield3>.
ENDIF.

IF <field4> IS ASSIGNED.
CLEAR: <reffield4>.
ENDIF.

APPEND l_param_key TO it_expand_nodes.

ENDIF.
ENDIF.
ELSE.
"If next level is empty so finish the hierarchy
PERFORM add_complete_line USING '' 2
CHANGING l_last_key.
l_add
= 'X'.
ENDIF.
ENDIF.


**** LEVEL 2
IF <field2> IS ASSIGNED.
IF <field3> IS ASSIGNED. "Level 3 is empty
IF <field2> IS NOT INITIAL.
IF <reffield2> NE <field2>.
PERFORM level2 USING l_param_key
2
v_icon2
-fieldname
CHANGING l_param2_key.

IF <field3> IS ASSIGNED.
CLEAR: <reffield3>.
ENDIF.

IF <field4> IS ASSIGNED.
CLEAR: <reffield4>.
ENDIF.

APPEND l_param2_key TO it_expand_nodes.

ENDIF.
ENDIF.
ELSE.
"If next level is empty so finish the hierarchy
PERFORM add_complete_line USING L_PARAM_KEY 3
CHANGING l_last_key.
l_add
= 'X'.
ENDIF.
ENDIF.


*** LEVEL 3
IF <field3> IS ASSIGNED.
IF <field4> IS ASSIGNED. "Level 4 is empty
IF <field3> IS NOT INITIAL.
IF <reffield3> NE <field3>.
PERFORM level3 USING l_param2_key
3
v_icon3
-fieldname
CHANGING l_param3_key .


IF <field4> IS ASSIGNED.
CLEAR: <reffield4>.
ENDIF.

APPEND  l_param3_key TO it_expand_nodes.

ENDIF.
ENDIF.
ELSE.
"If next level is empty so finish the hierarchy
PERFORM add_complete_line USING l_param2_key 4
CHANGING l_last_key.
l_add
= 'X'.
ENDIF.
ENDIF.



*** LEVEL 4
IF <field4> IS ASSIGNED.
IF <field5> IS ASSIGNED. "Level 4 is empty
IF <field4> IS NOT INITIAL.
IF <reffield4> NE <field4>.
PERFORM level4 USING l_param3_key
3
v_icon3
-fieldname
CHANGING l_param4_key .


IF <field5> IS ASSIGNED.
CLEAR: <reffield5>.
ENDIF.

APPEND  l_param4_key TO it_expand_nodes.

ENDIF.
ENDIF.
ELSE.
"If next level is empty so finish the hierarchy
PERFORM add_complete_line USING l_param3_key 4
CHANGING l_last_key.
l_add
= 'X'.
ENDIF.
ENDIF.


*** LEVEL 5
IF <field5> IS ASSIGNED.
IF <field5> IS NOT INITIAL.
IF <reffield5> NE <field5>.
PERFORM level5 USING l_param4_key
4
v_icon5
-fieldname
CHANGING l_param5_key .

APPEND  l_param4_key TO it_expand_nodes.

PERFORM add_complete_line USING l_param5_key 6
CHANGING l_last_key.
l_add
= 'X'.

ENDIF.
ENDIF.
ENDIF.

IF l_add IS INITIAL.
PERFORM add_complete_line USING l_param5_key 5
CHANGING l_last_key.
ENDIF.

IF <field1> IS ASSIGNED.
<reffield1>
= <field1>.
ENDIF.

IF <field2> IS ASSIGNED.
<reffield2>
= <field2>.
ENDIF.

IF <field3> IS ASSIGNED.
<reffield3>
= <field3>.
ENDIF.

IF <field4> IS ASSIGNED.
<reffield4>
= <field4>.
ENDIF.

IF <field5> IS ASSIGNED.
<reffield5>
= <field5>.
ENDIF.

CLEAR: l_add.

ENDLOOP.

 

*&---------------------------------------------------------------------*
*&      Form  LEVEL1
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_WA_DATA  text
*      -->P_L_PARAM2_KEY  text
*      -->P_3      text
*      -->P_1748   text
*      <--P_L_PARAM3_KEY  text
*----------------------------------------------------------------------*
FORM level1 USING  p_relat_key TYPE lvc_nkey
p_icon
CHANGING p_node_key.

DATA: l_node_text TYPE lvc_value,
relat      
TYPE int4,
wa_refe    
TYPE tab_type,
wa_level  
TYPE ty_output.

* set item-layout
DATA: lt_item_layout TYPE lvc_t_layi,
ls_item_layout
TYPE lvc_s_layi.

DATA: ls_node TYPE lvc_s_layn.
ls_node
-n_image   = space.
ls_node
-exp_image = space.

ls_item_layout
-t_image = p_icon.
ls_item_layout
-style   = cl_gui_column_tree=>style_intensified.
ls_item_layout
-fieldname = tree1->c_hierarchy_column_name.
APPEND ls_item_layout TO lt_item_layout.

* add node
l_node_text
= <field1>.
wa_level
-level   = 1.

ls_node
-isfolder = 'X'.

CALL METHOD tree1->add_node
EXPORTING
i_relat_node_key    
= p_relat_key
i_relationship      
= cl_gui_column_tree=>relat_last_child
i_node_text         
= l_node_text
is_outtab_line      
= wa_level
is_node_layout      
= ls_node
it_item_layout      
= lt_item_layout
IMPORTING
e_new_node_key      
= p_node_key
EXCEPTIONS
relat_node_not_found
= 1
node_not_found      
= 2
OTHERS               = 3.

ENDFORM.                    " LEVEL1

 


*&---------------------------------------------------------------------*
*&      Form  LEVEL2
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_L_PARAM1_KEY  text
*      -->P_2      text
*      -->P_1721   text
*      <--P_L_PARAM2_KEY  text
*----------------------------------------------------------------------*
FORM level2  USING p_relat_key TYPE lvc_nkey
hierarchy
icon
CHANGING p_node_key TYPE lvc_nkey.

* set item-layout
DATA: lt_item_layout TYPE lvc_t_layi,
l_node_text   
TYPE lvc_value,
ls_item_layout
TYPE lvc_s_layi,
relat         
TYPE int4,
wa_level     
TYPE ty_output.

ls_item_layout
-t_image = icon.
ls_item_layout
-style   cl_gui_column_tree=>style_intensified.
ls_item_layout
-fieldname = tree1->c_hierarchy_column_name.
APPEND ls_item_layout TO lt_item_layout.

* add node
l_node_text
= <field2>.
wa_level
-level = hierarchy.


relat
= cl_gui_column_tree=>relat_last_child.
CALL METHOD tree1->add_node
EXPORTING
i_relat_node_key
= p_relat_key
i_relationship  
= relat
i_node_text     
= l_node_text
is_outtab_line  
= wa_level
it_item_layout  
= lt_item_layout
IMPORTING
e_new_node_key  
= p_node_key.


ENDFORM.                    "LEVEL2


*&---------------------------------------------------------------------*
*&      Form  LEVEL3
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_L_PARAM1_KEY  text
*      -->P_2      text
*      -->P_1721   text
*      <--P_L_PARAM2_KEY  text
*----------------------------------------------------------------------*
FORM level3  USING p_relat_key TYPE lvc_nkey
hierarchy
icon
CHANGING p_node_key TYPE lvc_nkey.

* set item-layout
DATA: lt_item_layout TYPE lvc_t_layi,
l_node_text   
TYPE lvc_value,
ls_item_layout
TYPE lvc_s_layi,
relat         
TYPE int4,
wa_level     
TYPE ty_output.

ls_item_layout
-t_image = icon.
ls_item_layout
-style   cl_gui_column_tree=>style_intensified.
ls_item_layout
-fieldname = tree1->c_hierarchy_column_name.
APPEND ls_item_layout TO lt_item_layout.

* add node
l_node_text
= <field3>.


wa_level
-knuma   = wa_output-knuma.

CONCATENATE wa_output-datab+6(2)
wa_output
-datab+4(2)
wa_output
-datab(4)
INTO wa_level-zzbrandd
SEPARATED BY '.'.

CONCATENATE wa_output-datbi+6(2)
wa_output
-datbi+4(2)
wa_output
-datbi(4)
INTO wa_level-kunnr
SEPARATED BY '.'.

wa_level
-matnr   = wa_output-waers.
wa_level
-kondm   = wa_output-agnotes.
wa_level
-level   = hierarchy.

relat
= cl_gui_column_tree=>relat_last_child.
CALL METHOD tree1->add_node
EXPORTING
i_relat_node_key
= p_relat_key
i_relationship  
= relat
i_node_text     
= l_node_text
is_outtab_line  
= wa_level
it_item_layout  
= lt_item_layout
IMPORTING
e_new_node_key  
= p_node_key.


ENDFORM.                    " LEVEL3

*&---------------------------------------------------------------------*
*&      Form  LEVEL4
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_L_PARAM1_KEY  text
*      -->P_2      text
*      -->P_1721   text
*      <--P_L_PARAM2_KEY  text
*----------------------------------------------------------------------*
FORM level4  USING p_relat_key TYPE lvc_nkey
hierarchy
icon
CHANGING p_node_key TYPE lvc_nkey.

* set item-layout
DATA: lt_item_layout TYPE lvc_t_layi,
l_node_text   
TYPE lvc_value,
ls_item_layout
TYPE lvc_s_layi,
relat         
TYPE int4,
wa_level     
TYPE ty_output.

ls_item_layout
-t_image = icon.
ls_item_layout
-style   cl_gui_column_tree=>style_intensified.
ls_item_layout
-fieldname = tree1->c_hierarchy_column_name.
APPEND ls_item_layout TO lt_item_layout.

* add node

l_node_text
= <field4>.

wa_level
-zzbrandd = wa_output-combin.
wa_level
-level   = hierarchy.

wa_level
-knuma = wa_output-knuma.

relat
= cl_gui_column_tree=>relat_last_child.
CALL METHOD tree1->add_node
EXPORTING
i_relat_node_key
= p_relat_key
i_relationship  
= relat
i_node_text     
= l_node_text
is_outtab_line  
= wa_level
it_item_layout  
= lt_item_layout
IMPORTING
e_new_node_key  
= p_node_key.


ENDFORM.                    "LEVEL4

*&---------------------------------------------------------------------*
*&      Form  LEVEL5
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_L_PARAM1_KEY  text
*      -->P_2      text
*      -->P_1721   text
*      <--P_L_PARAM2_KEY  text
*----------------------------------------------------------------------*
FORM level5  USING p_relat_key TYPE lvc_nkey
hierarchy
icon
CHANGING p_node_key TYPE lvc_nkey.

* set item-layout
DATA: lt_item_layout TYPE lvc_t_layi,
l_node_text   
TYPE lvc_value,
ls_item_layout
TYPE lvc_s_layi,
relat         
TYPE int4,
wa_level     
TYPE ty_output.

ls_item_layout
-t_image = icon.
ls_item_layout
-style   cl_gui_column_tree=>style_intensified.
ls_item_layout
-fieldname = tree1->c_hierarchy_column_name.
APPEND ls_item_layout TO lt_item_layout.

* add node

l_node_text
= <field5>.

wa_level
-zzbrandd = wa_output-combin.
wa_level
-level   = hierarchy.

wa_level
-knuma = wa_output-knuma.

relat
= cl_gui_column_tree=>relat_last_child.
CALL METHOD tree1->add_node
EXPORTING
i_relat_node_key
= p_relat_key
i_relationship  
= relat
i_node_text     
= l_node_text
is_outtab_line  
= wa_level
it_item_layout  
= lt_item_layout
IMPORTING
e_new_node_key  
= p_node_key.


ENDFORM.                    "LEVEL5

*&---------------------------------------------------------------------*
*&      Form  add_cmplete_line
*&---------------------------------------------------------------------*
*       add hierarchy-level 3 to tree
*----------------------------------------------------------------------*
*      -->P_LS_SFLIGHT  sflight
*      -->P_RELEATKEY   relatkey
*     <-->p_node_key    new node-key
*----------------------------------------------------------------------*
FORM add_complete_line USING  p_relat_key TYPE lvc_nkey
hierarchy
CHANGING  p_node_key TYPE lvc_nkey.

DATA: l_node_text TYPE lvc_value.

* set item-layout
DATA: lt_item_layout TYPE lvc_t_layi,
ls_item_layout
TYPE lvc_s_layi.

ls_item_layout
-fieldname = tree1->c_hierarchy_column_name.
APPEND ls_item_layout TO lt_item_layout.

l_node_text
= wa_output-bonem.

IF l_node_text IS INITIAL.
MESSAGE s021(zgeral) DISPLAY LIKE 'E'.
LEAVE LIST-PROCESSING.
ENDIF.

DATA: ls_node TYPE lvc_s_layn.
ls_node
-n_image   = space.
ls_node
-exp_image = space.

wa_output
-level = hierarchy.

CALL METHOD tree1->add_node
EXPORTING
i_relat_node_key
= p_relat_key
i_relationship  
= cl_gui_column_tree=>relat_last_child
is_outtab_line  
= wa_output
i_node_text     
= l_node_text
is_node_layout  
= ls_node
it_item_layout  
= lt_item_layout
IMPORTING
e_new_node_key  
= p_node_key.
ENDFORM.                               " add_complete_line

 

 

 

I think that is everything here. Let me know if I missed something or someone need help to understand it.

Hope it helps.

Regards,

Andréa

ABAP Text Symbol Analysis Tool

$
0
0


SAP has provided inbuilt text symbols analysis tool. 

 

This tool is used for following purposes

a) To delete text symbols which are not used in report.

b) To create text symbols that needs to be added in text pool .

c) To correct text symbols which are defined repeatedly or differently.

 

Lets know more about the ABAP Text Symbol Analysis Tool

 

I have created a test report Z_TEXTSYMBOL_TEST for explaining the tool as below


 

In this report you can see there are 10 texts.  “Text1”, “Text5” , “Text6” and “Text9” with text id 001,005,006 and 009 respectively. However, “Text6” is defined differently in text pool as below:



   


 

Also you can notice that text “Unused Text” with Text ID 011 is not used in report.

 

 

a) Delete unused text from text pool


     Using “Delete Row” button we delete the text. However for large size reports, it is difficult to find

     which text symbol is not used in report.

     To delete unused text symbols from text pool go to SE38. Type report name (e.g.

     Z_TEXTSYMBOL_TEST) and select “Text elements” radio button and click on change.


 

 

You can see the change text symbol screen. Now click on “Compare Text Symbols”  shown in below figure or press key Ctrl+Shift+F7 to start ABAP Text Symbol Analysis Tool



 

You can see the ABAP Text Symbol Analysis screen as below.



You can also access this tool by SE32 transaction as below:

Goto SE32 transaction an click on "Anlysis".

 

 

 

 

Now select first radio button i.e. “Text symbols that can be deleted from the text pool” and click on “Edit” button.

 

 

You can see below screen.

 

Now check the text symbols which you want to delete from the text pool and click on delete symbol. You will get below message “Text symbols may still be used elsewhere. Delete anyway”.  Click on “Yes” to delete the selected texts.

 

 

If you select all the unused texts and deleted them then you will see below screen which means now there are no unused text symbols present in text pool



 

 

b) To create text symbols that needs to be added in text pool.


Now same way select second radio button “Text symbols that need to be added to the text pool” and click on “Edit”. Then you will see all the texts with text id e.g. “Text1” (001) present in report which are not added in the text pool as below

 

 

Now check texts which you want to add in text pool and click on create as below



 

After creating selected texts in text pool you can see below screen which shows there are no texts with text id in report which are not maintained in text pool.

 

 

 

 

c)  To correct text symbols which are defined repeatedly or differently.

 

Select third option “Text symbols defined repeatedly/differently in program” and click on edit to see texts which are maintained differently in report than the text pool as below:

 

 

You can see for text id 006 there are two texts.  The unmarked text will be replaced with the marked text.

Here “Text6” is marked as we want to maintain Text6 for text id 006. Click on replace as shown below to replace “Defined Differently in report” with “Text6”.



 

Now you can see all inconsistencies in text pool got corrected as below



 

Now click on save to save the changes made in text pool. If you go back you can see updated text pool as below:

 

Huge File Reader Now Part of apab2xlsx Package

$
0
0

 

This is a continuation of my blog A Way of Reading Huge Excel Files in which I described the usage of the sXML parser for an alternative Excel file reader with a massively reduced memory footprint. I followed the suggestion of some of my blog readers (Kay Streubel, Rainer Hübenthal) to add the code to the abap2xlsx repository at ivanfemia/abap2xlsx · GitHub. The abap2xlsx package now contains a subclass ZCL_EXCEL_READER_HUGE_FILE of the existing reader class ZCL_EXCEL_READER_2007.


I owe special thanks to Ivan Femia for helping me with the first steps into the abap2xlsx realm. :-)


Only the sheet data and shared string table are parsed differently in ZCL_EXCEL_READER_HUGE_FILE, as these usually are the heavy-load parts of an Excel file. The other parts - like style details - continue to be analyzed in the superclass and are therefore parsed with iXML parsers, as before.

 

How to Use It

 

The public interface - and therefore the usage - is the same as for ZCL_EXCEL_READER_2007. If you have an XSTRING with the .xlsx file content, you work with the LOAD( ) method; if you have a filename and want abap2xlsx to read it (from application or presentation server), you would use LOAD_FILE( ).

 

The test report ZZ_TEST_EXCEL_READER (code below) shows the idea.

 

 

It's Just Another Object Type

 

You can use the report to load an excel file from the presentation or application server, doing the parsing with the "standard" reader or with the new "huge file reader". As you see, the difference between the two reader versions is only in the CREATE OBJECT lo_reader TYPE ... statement.

 

Decision to Read Files From Presentation Server or Application Server

 

The possibility to decide between application server and presentation server explicitly is a new feature. Formerly, this distinction was made automatically, depending on the SY-BATCH flag. This will still be the default value for the new optional import parameter I_FROM_APPLSERVER. But now, the distinction can also be made by the caller.

 

It's good practice to have the reader as a local object in an own, separate code unit (here: a form routine). This way, all the memory that was necessary during the read process will be freed when the excel object has been built and the reading unit is left.

 

 

report zz_test_excel_reader.
parameters:   p_file  type string lower case default `C:\temp\test.xlsx`,   p_appl  type flag,   p_huge  type flag.
start-of-selection.   perform start.
* ---
form start.   data: lo_excel type ref to zcl_excel,         lo_excpt type ref to zcx_excel.   try.       perform read using p_file p_appl p_huge                    changing lo_excel.       break-point. " <<<--- Inspect lo_excel here
* Parsed data are usually contained in the following ITAB:
* LO_EXCEL->WORKSHEETS->WORKSHEETS->COLLECTION[1]->SHEET_CONTENT     catch zcx_excel into lo_excpt.       message lo_excpt type 'I'.   endtry.
endform.                    "start
* ---
form read using i_filename           type csequence                 i_from_applserver    type flag                 i_huge               type flag           changing e_excel           type ref to zcl_excel           raising zcx_excel.
* Use the reader instance as a local variable in a separate unit,
* so its memory will be released after leaving the unit.   data: lo_reader type ref to zif_excel_reader.   if i_huge eq 'X'.     create object lo_reader type zcl_excel_reader_huge_file.   else.     create object lo_reader type zcl_excel_reader_2007.   endif.   e_excel = lo_reader->load_file( i_filename         = i_filename                                    i_from_applserver = i_from_applserver ).
endform.                    "read

 

The Main Loop

 

An Excel workbook is saved as a zip archive containing several XML files. The files sheet1.xml, sheet2.xml etc. contain the actual worksheet data, among other things like style definitions. Here is a typical example of a sheet.xml: As you see, the data start with a separate element named sheetData; in this example, the cells contain pointers to the shared string table (which is defined in another file sharedStrings.xml).

 

sheetData.png

 

The parser (we use "token-based sXML parsing", see ABAP Keyword Documentation) for this file can safely skip forward until it meets the opening of element sheetData. Once this is reached, it should read elements and their contents one after another, as follows:

  • When an opening c element is detected, a new cell data structure has to be filled from the element c's attribute values;
  • When a text content of an element is detected, this content should usually be treated as value of the current cell
    • in some cases, however, the value has to be fetched by index from the shared string table
  • When a closing c element is detected, the current cell data should be added into the ABAP worksheet representation
  • When a closing sheetData element is detected, the loop should be left.

 

And this is the above text written in ABAP:

 

method read_worksheet_data.   data: ls_cell   type t_cell.
* Skip to <sheetData> element   skip_to(  iv_element_name = `sheetData`  io_reader = io_reader ).
* Main loop: Evaluate the <c> elements and its children   while io_reader->node_type ne c_end_of_stream.     io_reader->next_node( ).     case io_reader->node_type.       when c_element_open.         if io_reader->name eq `c`.           ls_cell = fill_cell_from_attributes( io_reader ).         endif.       when c_node_value.         case io_reader->name.           when `f`.             ls_cell-formula = io_reader->value.           when `v`.             if ls_cell-datatype eq `s`.               ls_cell-value = get_shared_string( io_reader->value ).             else.               ls_cell-value = io_reader->value.             endif.           when `t` or `is`.             ls_cell-value = io_reader->value.         endcase.       when c_element_close.         case io_reader->name.           when `c`.             put_cell_to_worksheet( is_cell = ls_cell io_worksheet = io_worksheet ).           when `sheetData`.             exit.         endcase.     endcase.   endwhile.
endmethod.

 

A caveat for the sXML parser is necessary: the instance attributes like name, value, etc. are never initialized. The caller has to take care which values are outdated, by looking at the three events "element open", "element close" and "node value".

 

When using the sXML reader, special caution is necessary for elements with mixed content. In the example,

 

<x><y>v</y>w</x>


the following events will be raised:


 

Event co_nt_element_open  name: x   value:
Event co_nt_element_open  name: y   value:
Event co_nt_value         name: y   value: v
Event co_nt_element_close name: y   value: v
Event co_nt_value         name: y   value: w
Event co_nt_element_close name: x   value: w
Event co_nt_final         name: x   value: w

 

Observe that when the event for value 'w' is raised, the name attribute still contains 'y', although y has already been closed and thus is an outdated value: The sXML parser doesn't keep track of this outdating - if it would, it had to keep track of the document structure (for example by maintaining a stack of the parents of the current node). In the sXML philosophy, however, all things regarding knowledge of the document structure, are left to the caller - the reading process is restricted to the detection of opening and closing elements, of attribute values, text nodes etc.

 

Some Remarks on Tests

 

A parser is an ideal candidate for unit tests, since its output depends only on the input stream - there are usually no dependencies on other states (database, API calls, ...). So I implemented all code of the reader using unit tests.

 

Testing Private Methods

 

The huge file reader has no public methods on its own - the only public methods are inherited from ZCL_EXCEL_READER_2007,  which is out of the scope of my tests. For this reason, I decided to test private methods. There is a price for this decision: We are not as free as it could be in renaming and deleting private methods, since always the test classes have to be adapted.

 

To make private methods testable, we start the unit test section with a declaration of friendship:

 

class lcl_test definition deferred.
class zcl_excel_reader_huge_file definition local friends lcl_test.

Reading a Number

 

With some help methods in the test class, I let the code of a test look more intentional and more to-the-point: Everybody can read from the code of the following test method that the cell datatype "number" is under test: A cell with numerical content (17) should propagate the worksheet in the expected way:

**

   method test_number.     data lo_reader type ref to if_sxml_reader.     lo_reader = get_reader(       `<c r="A1" t="n"><v>17</v></c>`       ).     out->read_worksheet_data( io_reader = lo_reader io_worksheet = worksheet ).     assert_value_equals( `17` ).     assert_datatype_equals( `n` ).
endmethod.                    "test_shared_string

 

Here, OUT is the "object under test". As usual for unit tests, a new reader instance is created for each test method in the setup method. The same holds for the worksheet instance here, which holds the result.

 

Many tests just call the private central method read_worksheet_data( ) and then inspect the produced worksheet instance. This method imports an instance of an sXML reader to read the input data, and an instance of ZCL_EXCEL_WORKSHEET to write the result. Apart from the expected changes in the worksheet's state, there are no side effects on other data. And the only further external dependency is that the table of shared strings (supposed to be parsed earlier from the sharedStings.xml file) should be available already in the private attribute shared_strings, a simple list of strings (of type STRINGTAB).

 

In order to restrict the code of the test methods to the essential, specific parts, the get_reader( ) method only receives the XML code snippet for the  cell to be parsed (or multiple cells in a row): before creating the reader instance, this nucleus is wrapped into a document skeleton, to emulate a full XML document:

 

 

*  method get_reader.    data: lv_full type string.    concatenate `<root><sheetData><row>` iv_xml `</row></sheetData></root>` into lv_full.    eo_reader = cl_sxml_string_reader=>create( cl_abap_codepage=>convert_to( lv_full ) ).  endmethod.                    "get_reader

 

On Shared Strings and the Boundary Values Problem

 

As a local friend, the test class is allowed to pre-fill the shared_strings table attribute. Therefore, we can pretend to have already parsed the sharedStrings.xml file by adding some values to the shared_strings table. Afterwards, we call the reader and check that a cell with a string reference is correctly resolved. Here is the test for correct reading of the string table:

 

 

*  method test_shared_string.    data lo_reader type ref to if_sxml_reader.    append `Test1` to out->shared_strings.    append `Test2` to out->shared_strings.    lo_reader = get_reader(      `<c r="A1" t="s"><v>1</v></c>`      ).    out->read_worksheet_data( io_reader = lo_reader io_worksheet = worksheet ).    assert_value_equals( `Test2` ).    assert_datatype_equals( `s` ).  endmethod.                    "test_shared_string

 

 

For shared strings, the content of the <v> element is the 0-based index for reading the string table. Therefore, the cell content in this example has to be 'Test2' after parsing.

 

In a former version, I had been too minimalistic: I used only one entry in the string table, and the <v> element contained 0, not 1, to point to this one entry.


The test in its former version passed, but it didn't detect a bug in the code, due to the "boundary value" anti-pattern: 0 is a special value, since it also is the initial value for integer variables. The bug was that the index was not transferred at all to the variable holding the index, which thus remained on its initial value, zero.  Only by extending the test with a second string and working with the non-special index value 1 instead, I could reproduce the bug - and then fix it.

 

Test Coverage

 

While working on the code and testing it, it's good to have a look on the test coverage. It shows possible uncovered coding extracts in which a bug might be hidden.

 

coverge.png

Of course, some parts are intentionally uncovered. In this class, the methods load_shared_strings( ), load_worksheet( ) and get_sxml_reader( ) have a zero coverage on purpose, since they provide data from outside. I only test how the class works on these data. But the rest of the methods  have a 100% statement coverage.

 

 

The branch coverage (a column which is hidden by default in the coverage analyzer's default layout) shows that some conditions could still be added. Indeed, I have no particular tests for invalid sheet.xml documents - for example, I always expect that there is sheetData element, as the Open XML specification requires.

 

As the boundary value problem above shows, full statement coverage - or even full branch coverage - is not sufficient for detecting all bugs sleeping in the code. An instinct for potential bugs is necessary while writing the test method. It's an interesting change of hat: when writing test methods, we are supposed to turn into the most fervent critics of ourselves!

Update Purchase requisition based on external operation of production order

$
0
0

During our recent ECC implementation, business requested that Service Material value (populated in user field : AFVGD-USR00) should be auto populated into a purchase requisition for an external operation.

 

The Purchase Requisition (PR) is created as soon as a Production order lines having Operation control keys relevant for external processing such as ‘EXTL’ or ‘EXTP’ is saved. In Standard SAP, PR is created with Operation text as a Material Description and it does not carry Material code in the PR material Code field. But to provide better visibility to buyer and supplier, business requested a PR to be created from Production order with external operations should address the following.

 

Purchase Requisition should be

  • Auto populated Material code in PR with material code maintained in the “Service Matl” field in the Operation details screen for the external Operation number in Production Order(ref the screen Shot)

screenshot1.png

 

screenshot2.png

  • Item long text: material text both short and long copied from the material master, engineering revision level to copy from basic data 1 of the material master.

screenshot3.png

 

The business also requested some pre-validations when performing auto population of material code.

For e.g.:

1. The service material code should begin with ‘SM_’. 

2. Material should be available in MARA table.

3. Material and plant combination should exist in MARC table.

 

The validation errors should be visible on the operation details screen of the production order.

 

It took me some time to debug the end to end transaction to find out appropriate enhancement points to achieve different business requirements. I am listing down the enhancements used in table below.

I hope this is useful for others attempting to map similar requirements during automatic PR created\changed when Production Order is created\changed. 

 

Enhancement Details:

Sequence

Enhancement Type

Enhancement Name

Purpose

1

User - Exit

EXIT_SAPLCOVG_001

  • We used this exit to collect all modified external operations records.
  • This exit is triggered only when an external operation is modified.
  • However there were few scenarios in which this exit is not triggered at all and hence we have also implemented EXIT_SAPLCOZF_001.

E.g. when there is no change to external operation or when an external operation is deleted etc.

  • Populate external operation data obtained in this exit into a global internal table using custom method.

2

Spot

Enhancement spot - ES_SAPLCOZF

Point - \PR:SAPLCOZF\EX:SAPLCOZF_OLC_002\EI

  • This enhancement spot is triggered when an external operation is deleted.
  • Export deleted external operation record (AFVGD_IMP) and respective deletion flag (DELKNZ_BANF) using ABAP memory id.

3

User - Exit

EXIT_SAPLCOZF_001

  • This exit is triggered for each external operation once.
  • We used this exit to collect external operations records which cannot be fetched using exit EXIT_SAPLCOVG_001.
  • However this exit is not triggered when an existing external operation record is modified. So in that case exit EXIT_SAPLCOVG_001 will be useful.
  • Also import deleted external operations using ABAP memory id. (This data is exported by 2nd enhancement).
  • Populate external operation data obtained in this exit into a global internal table using custom method.

4

Badi

WORKORDER_UPDATE

Method – AT_SAVE

  • This method is used for adding any custom validation on external operation fields.
  • By pass the validation for deleted external operation with the help of deletion flag.
  • Fetch all the external operation from global internal table populated using above 2 exits.
  • Appropriate error message can be displayed using this Badi method.
  • If all the external operations are valid then export the external operation internal table using ABAP memory id for further processing. 

5

Spot

Enhancement spot – ES_SAPLEBNE

Point - \PR:SAPLEBNE\EX:LEBNEF08_01\EI

  • This enhancement spot is triggered before execution of standard FM ‘ME_UPDATE_REQUISITION’ used for updating PR data.
  • Import valid external operation table using ABAP memory id.
  • Modify purchase requisition table XEBAN using external operation data.
  • In our business scenario we modified PR item material number and short description using external operation fields.

6

Badi

WORKORDER_UPDATE

Method – IN_UPDATE

  • At this point, the PR is already created and hence this method can be used to populate various long texts available in PR.
  • In our business scenario, we used this method to update PR item long text based on external operation records.

Checking for Duplicate Sales Orders in SAP CRM

$
0
0

1.   Introduction

 

While creating sales orders from SAP or in WUI it is often possible that similar sales orders are placed more than once. In that case it would be nice to have a preventive measure by checking in the system if there are any existing sales orders that are similar to the one that is being created. Finding one probable duplicate order and being warned about the same will ensure that before we save a new order.

 

1. MAJOR FEATURES

 

When a sales order is entered in CRM, before saving it, it may be necessary to perform a duplicate order check in BADI ORDER_SAVE, depending on the conditions described below.

The duplicate order check should be activated for the combination of transaction type, sales area and media type. (For example: for standard orders within France the check is activated, for standard orders within the UK the check is deactivated).

Sales Area – The sales area provides a mechanism to turn the functionality on or off per country.

Transaction Type – Should be checked to make the difference between sales orders. (For example: excluding debit or credit memo’s.

Media Type – E.g. Fax, E-mail, telephone. (For example: web orders and EDI will be excluded

Contact Person – The check on contact person should be turned on or off per activation area above. Example: When the check on contact person is switched ON the check will only consider orders created for the same ship-to and contact person. When the check is turned OFF there is a check between all orders for a certain ship-to.

 

Number of days – A number of days should be included to know how many days back in time should be checked. This can differ per activation area. EG for France 8 days, for UK 4 days.

Number of items – In the as-is situation 4 items are checked. If 4 items are identical in 2 orders, the duplicate check sees the order as a possible duplicate order. In the to-be situation it must be possible that the number of items is variable per activation area above.

1. TECHNICAL DETAILS

 

For the purpose of the duplicate order check, key figures like sales organization, distribution channel, division, transaction type and media type is looked into for the order that is to be saved.

 

This is done using a table where certain flags and ranges are stored. For example, if the flag duplicate_order_check is enabled this means duplicacy needs to be checked. Similarly, if contact_per_chk is enabled then the contact person has to be compared as well. The days_chk determines maximum how many days back we should check for duplicates and for items_chk determines how many items need to be similar in order to qualify as a duplicate.

 

scn1.jpg

 

Once a duplicate order is found, the customer would like to be warned that what they are trying to create might already exist in the system. This can be done simply by using a popup message box. This part we can come it later but the main requirement is to find the details of the current sales order that is about to be saved.

 

 

CRM ABAP

In the SAP system it is possible using BADI ORDER_SAVE->CHECK_BEFORE_SAVE. So we need to implement this badi first.

 

 

scn1.jpg

In the method CHECK_BEFORE_SAVE the importing parameter in concern is IV_GUID. So all the details of the order need to be fetched through the GUID.

 

First and foremost, this is only applicable in foreground and to prevent this we check.

 

 

 


IF sy-batch IS NOT INITIAL.
RETURN.
ENDIF.

 

Some of the data can be retrieved from the SAP memory.

 

* Fetch partner data to find
* determine layout
CALL FUNCTION 'COM_PARTNER_LAYOUT_GET_UI'
EXPORTING
iv_subscreen_id   = lco_scr1000
IMPORTING
es_screen_layout  = lwa_screen_layout
EXCEPTIONS
no_layout_defined = 1
  OTHERS            = 2.

  IF sy-subrc NE 0.
* screen data could not be fetched so return
RETURN.
ENDIF.

* when its a new order object id is initial
IF lwa_screen_layout-object_id IS NOT INITIAL.
RETURN.
ENDIF.

 

 

This data helps us to find the details of the partners like sold to party, ship to party, contact etc.

 

* get partners
CALL FUNCTION 'COM_PARTNER_TO_DISPLAY_OW'
EXPORTING
iv_partnerset_guid   = lwa_screen_layout-partnerset
  iv_determ_proc       = lwa_screen_layout-determ_proc
IMPORTING
  et_partner           = lt_partner_to_disp
EXCEPTIONS
partnerset_not_found = 1
  OTHERS = 2.
IF sy-subrc NE 0.
* screen data could not be fetched so return
RETURN.
ENDIF.

lt_partner_to_display[] = lt_partner_to_disp[].

* get ship to party
READ TABLE  lt_partner_to_display
INTO lwa_partner_to_display
WITH KEY partner_fct = lco_partner_2.
IF sy-subrc EQ 0.
lv_ship_to_c = lwa_partner_to_display-partner_number.
ENDIF.


* get contact person
READ TABLE lt_partner_to_display
INTO lwa_partner_to_display
WITH KEY partner_fct = lco_partner_15.
IF sy-subrc EQ 0.
lv_contact_c = lwa_partner_to_display-partner_number.
ENDIF.

 

But we will need the GUID to fetch additional information

 

* Fetch sales org/dis channel/division from orgman
INSERT iv_guid
INTO TABLE lt_header_guid.

  CALL FUNCTION 'CRM_INTLAY_GET_DATA'
EXPORTING
iv_guid = iv_guid
  iv_kind           = lco_kind_a
iv_interfacename  = lco_int_orgman
IMPORTING
es_interfacevalue = lwa_orgman_ui
EXCEPTIONS
error_occurred    = 1
no_valid_guid     = 2.
IF sy-subrc EQ 0.
lv_sales_org_c        = lwa_orgman_ui-sales_org_short.
lv_dis_channel_c      = lwa_orgman_ui-dis_channel.
  lv_division_c         = lwa_orgman_ui-division.
ELSE.
* failed to fetch organization data so return
RETURN.
ENDIF.

* Fetch category(media type) from sales UI
CALL FUNCTION 'CRM_INTLAY_GET_DATA'
EXPORTING
  iv_guid           = iv_guid
  iv_kind           = lco_kind_a
iv_interfacename  = lco_int_sales
IMPORTING
es_interfacevalue = lwa_sales_ui
EXCEPTIONS
error_occurred    = 1
no_valid_guid     = 2.
IF sy-subrc EQ 0.
lv_media_type_c       = lwa_sales_ui-category.
ELSE.
* failed to fetch media type data so return
RETURN.
ENDIF.
* Transaction type
lv_transaction_type_c = lwa_screen_layout-process_type.

 

This gives us almost all information we require for checking the duplicate order, except the item level check. This is done using the standard function

 

  CALL FUNCTION 'CRM_ORDER_READ'

EXPORTING

it_header_guid       = lt_header_guid

IMPORTING

et_orderadm_h        = lt_orderadm_h

et_orderadm_i        = lt_orderadm

et_product_i         = lt_product_i

EXCEPTIONS

document_not_found   = 1

error_occurred       = 2

document_locked      = 3

no_change_authority  = 4

no_display_authority = 5

no_change_allowed    = 6

OTHERS = 7.

  IF sy-subrc <> 0.

* unable to fetch posting date so return

RETURN.

  ELSE.

READ TABLE lt_orderadm_h

INTO lwa_orderadm_h

INDEX 1.

  ENDIF.

 

In the next step we can check on the duplicate order table to check if the checking is enabled for the current order. In case duplicate check is disabled, we need not proceed further.

 

* Fetch data from switch table
SELECT SINGLE *
FROM yorder_check
INTO lwa_duplicate
WHERE sales_org         = lv_sales_org_c
AND dis_channel       = lv_dis_channel_c
AND division          = lv_division_c
AND transaction_type  = lv_transaction_type_c
AND media_type = lv_media_type_c.

* check if record is there and duplicate check is active else return
IF sy-subrc NE 0
OR lwa_duplicate-duplicate_ord_chk IS INITIAL.
RETURN.
ENDIF.

 

 

Once we know that we have to check for duplicates, we start looking for existing records in between today's date and the date before with days check.

 

* determine posting date range
lwa_post_dt-sign         = lco_range_i.
lwa_post_dt-option       = lco_range_bt.
lwa_post_dt-low          = lwa_orderadm_h-posting_date - lwa_duplicate-days_chk.
lwa_post_dt-high         = sy-datum.
APPEND lwa_post_dt TO lt_post_dt.

* select GUIDs from orderadm_h table
SELECT *
FROM crmd_orderadm_h
INTO TABLE lt_header
WHERE posting_date IN lt_post_dt
AND process_type EQ lv_transaction_type_c.

  IF lt_header[] IS INITIAL.
RETURN.
ENDIF.

 

Now that we have lt_header[] with all the sales orders which fall within the same date range we can fetch all information related to these orders.

 

* header has values this means records exist within the date range
LOOP AT lt_header INTO lwa_header.
APPEND lwa_header-guid TO lt_guid_sorted.
ENDLOOP.
SORT lt_guid_sorted.
DELETE ADJACENT DUPLICATES FROM lt_guid_sorted.
lt_header_guid[] = lt_guid_sorted[].
* read the record details
CALL FUNCTION 'CRM_ORDER_READ'
EXPORTING
  it_header_guid       = lt_header_guid
IMPORTING
  et_activity_h        = lt_activity2
  et_orderadm_i        = lt_orderadm2
  et_product_i         = lt_product_i2
et_orgman = lt_orgman2_s
  et_partner           = lt_partner2
EXCEPTIONS
document_not_found   = 1
  error_occurred       = 2
document_locked      = 3
no_change_authority  = 4
no_display_authority = 5
no_change_allowed    = 6
  OTHERS = 7.
IF sy-subrc <> 0.
* unable to fetch record details so return
RETURN.
ENDIF.

lt_orgman2[] = lt_orgman2_s[].

 

Now that we have all the details for the selected orders, we simple need to compare them

 

  LOOP AT lt_header INTO lwa_header.
* Clear variables and looping tables
CLEAR:
lv_ship_to_v,
lv_sales_org_v,
lv_dis_channel_v,
lv_division_v,
lv_transaction_type_v,
lv_media_type_v,
lv_contact_v.

* Now that we have the partner list in lt_partner2
* fetch ship to party and check
READ TABLE lt_partner2
INTO lwa_partner2
WITH KEY partner_fct     = lco_partner_2
  ref_guid        = lwa_header-guid.
IF sy-subrc EQ 0.
lv_ship_to_v = lwa_partner2-partner_no.
ENDIF.


IF lv_ship_to_c NE lv_ship_to_v.
CONTINUE.
ENDIF.

* fetch orgainzational data
READ TABLE lt_orgman2
INTO lwa_orgman2
WITH KEY sales_org_ori   = lco_sori_a
  ref_guid        = lwa_header-guid.
IF sy-subrc EQ 0.
  lv_sales_org_v         = lwa_orgman2-sales_org_short.
  lv_dis_channel_v       = lwa_orgman2-dis_channel.
  lv_division_v          = lwa_orgman2-division.
ENDIF.


* if organizational data does not match check next record
IF lv_sales_org_c   NE lv_sales_org_v
OR lv_dis_channel_c NE lv_dis_channel_v
OR lv_division_c    NE lv_division_v.
CONTINUE.
ENDIF.

* Transaction type
lv_transaction_type_v  = lwa_header-process_type.

* fetch media type
READ TABLE lt_activity2
INTO lwa_activity2
WITH KEY guid = lwa_header-guid.
IF sy-subrc EQ 0.
  lv_media_type_v        = lwa_activity2-category.
ENDIF.

* Compare and check items
IF lv_transaction_type_c = lv_transaction_type_v
AND lv_media_type_c       = lv_media_type_v.

* When similar record exist in existing and current line items
* create table for current line items
REFRESH: lt_items, lt_items2.
LOOP AT lt_orderadm INTO lwa_orderadm.
lwa_items-ordered_prod  = lwa_orderadm-ordered_prod.
CLEAR lwa_product_i.
READ TABLE lt_product_i
INTO lwa_product_i
WITH KEY guid = lwa_orderadm-guid.
IF sy-subrc EQ 0.
  lwa_items-net_weight    = lwa_product_i-net_weight.
ENDIF.
APPEND lwa_items TO lt_items.
ENDLOOP.
SORT lt_items.
* create table for existing line items
LOOP AT lt_orderadm2 INTO lwa_orderadm2.
IF lwa_orderadm2-header = lwa_header-guid.
lwa_items2-ordered_prod = lwa_orderadm2-ordered_prod.
CLEAR lwa_product_i2.
READ TABLE lt_product_i2
INTO lwa_product_i2
WITH KEY guid = lwa_orderadm2-guid.
IF sy-subrc EQ 0.
  lwa_items2-net_weight    = lwa_product_i2-net_weight.
ENDIF.
APPEND lwa_items2 TO lt_items2.
ENDIF.
ENDLOOP.
SORT lt_items2.
* now that we have the product and their quantities in sorted order
* we have to look for matching matching items
CLEAR lv_match.
LOOP AT lt_items INTO lwa_items.
READ TABLE lt_items2
TRANSPORTING NO FIELDS
WITH KEY ordered_prod    = lwa_items-ordered_prod
  net_weight      = lwa_items-net_weight.
IF sy-subrc = 0.
  lv_match = lv_match + 1.
DELETE lt_items2 INDEX sy-tabix.
ENDIF.
ENDLOOP.

* if there are more matching records lv_match than lwa_duplicate-items_chk
* or equal, then we say that its a duplicate record
IF lv_match GE lwa_duplicate-items_chk.
* check if contact person check is active, if yes then check contacts
IF lwa_duplicate-contact_per_chk IS NOT INITIAL.
* fetch contact data
READ TABLE lt_partner2
INTO lwa_partner2
WITH KEY partner_fct = lco_partner_15
  ref_guid = lwa_header-guid.
IF sy-subrc EQ 0.
lv_contact_v = lwa_partner2-partner_no.
ENDIF.

IF lv_contact_c = lv_contact_v.
* show message
lv_message = text-t01.
REPLACE ALL OCCURRENCES OF '&' IN lv_message WITH lwa_header-object_id.
CALL FUNCTION 'POPUP_TO_CONFIRM'
  EXPORTING
  text_question         = lv_message
  display_cancel_button = ''
  IMPORTING
  answer = lv_answer.
IF lv_answer NE lco_ans_1.
  RAISE do_not_save.
ENDIF.
RETURN.
ENDIF.
ELSE.
* when contact person check is not active
* show message
lv_message = text-t01.
REPLACE ALL OCCURRENCES OF '&' IN lv_message WITH lwa_header-object_id.
CALL FUNCTION 'POPUP_TO_CONFIRM'
EXPORTING
  text_question         = lv_message
  display_cancel_button = ''
IMPORTING
  answer = lv_answer.
IF lv_answer NE lco_ans_1.
RAISE do_not_save.
ENDIF.
RETURN.
ENDIF.
ENDIF.
ENDIF.
ENDLOOP.

As we can see that the program generates a popup message in case a possible duplicate order is found. The order number is reflected in the popup message asking whether to continue saving or not. In case we choose yes, the order is saved irrespective of possible duplicacy, else, an exception is raised and this causes the order not to be saved.

 

In this case, we get a popup in the SAP system while trying to save a new (or copied) order using CRMD_ORDER. If yes it will save, else it wont save

 

scn1.jpg

Viewing all 943 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>