1 2 3 43 Previous Next

ABAP Development

634 Posts


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

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 function module K_COSTCENTER_SHOW.

 

KE53 Display Profit Center

Consider function module PCA_MASS_DISPLAY_OBJECT.

 

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.

 

MIGO Goods Movement

Consider function module MIGO_DIALOG.

 

 

Challenge section

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

 

Long term challenge: replace the proposed functions above with their released equivalents.

 

Credits: All the ladies and gentleman who joined the discussion below. I will not copy all the names here but it is the very nature of this article to collect valuable pieces of knowledge from our "SCN team" members and they receive all the credit. Thank you all!

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.

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.

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.

 















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 in table TBO00. 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

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- An idoc 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


Sometimes we need to search for the use of RFCs but where-used is only available if function modules exist in caller system.


One solution is using ABAP source scan technique with available reports for your SAP platform and version. Depending of your search criteria this can take long processing time and spend system resources.

 

To be possible to use where-used I build this tool using existent repository information system functions to find nonexistent remote function modules. Try it, is very fast and support direct object code navigation.

 

 

How to implement:

  1. Create a executable report in your SAP development system with name ZNM_FIND_RFC and description Search RFCs calls using transaction SE38;
  2. Copy past code bellow and activate the program;
  3. Create/Fill corresponding Text Symbols and Selection Texts. Please check the meaning at top comments of the program.


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_find_rfc MESSAGE-ID 00.

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

* Created by NM to Search RFCs calls

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

* Text Symbols

* A01 Description

* A02 Desc.

* A03 Sub Object

* B01 RFC Selection

* C04 Rotine

* M01 Not found

* M02 Error displaying result

*

* Selection Texts

* P_RFC Function

*

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

* GLOBAL DATA

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

TYPE-POOLS abap. "Only for old versions

 

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

DATA gt_founds TYPE TABLE OF rsfindlst.                     "#EC NEEDED

 

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

* CLASS gcl_handle_events DEFINITION

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

CLASS gcl_handle_events DEFINITION FINAL.

 

  PUBLIC SECTION.

    METHODS on_double_click FOR EVENT double_click OF cl_salv_events_table

      IMPORTING row column.                                 "#EC NEEDED

 

ENDCLASS.                    "lcl_handle_events DEFINITION

 

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

* SELECTION SCREEN

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

*------------------------------------------------- RFC Function Module *

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

SELECTION-SCREEN SKIP 1.

PARAMETERS p_rfc TYPE rs38l_fnam OBLIGATORY. "Name of Function Module

SELECTION-SCREEN SKIP 1.

SELECTION-SCREEN END OF BLOCK b01.

 

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

* REPORT EVENTS

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

START-OF-SELECTION.

  PERFORM search_rfc.

 

END-OF-SELECTION.

  PERFORM display_results.

 

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

* CLASS lcl_handle_events IMPLEMENTATION

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

CLASS gcl_handle_events IMPLEMENTATION.

 

*---------- Row dbclick ----------*

  METHOD on_double_click.

 

    DATA:

      ls_founds  LIKE LINE OF gt_founds,  "Found object

      lt_report  TYPE TABLE OF string,    "Report source code

      lt_results TYPE match_result_tab,   "Match results

      ls_results TYPE match_result.

 

*---------- Get selected line  ----------*

    READ TABLE gt_founds INTO ls_founds INDEX row.

    IF sy-subrc IS INITIAL.

 

*---------- Find position  ----------*

      READ REPORT ls_founds-object INTO lt_report.

      FIND p_rfc IN TABLE lt_report RESULTS lt_results.

      READ TABLE lt_results INTO ls_results INDEX 1.

 

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

      CALL FUNCTION 'RS_TOOL_ACCESS'                        "#EC FB_RC

        EXPORTING

          operation           = 'SHOW'

          object_name         = ls_founds-object

          object_type         = 'PROG'

          position            = ls_results-line

        EXCEPTIONS

          not_executed        = 1

          invalid_object_type = 2

          OTHERS              = 3.

    ENDIF.

 

  ENDMETHOD.                    "on_double_click

 

ENDCLASS.                    "lcl_handle_events IMPLEMENTATION

 

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

* FORMS

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

 

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

*&      Form  SEARCH_RFC

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

FORM search_rfc .

 

  CONSTANTS lc_obj_type TYPE seu_obj VALUE 'FF'.

 

  DATA:

    lt_findstring TYPE TABLE OF rsfind,

    ls_findstring LIKE LINE OF lt_findstring.

 

  ls_findstring-object = p_rfc.

  APPEND ls_findstring TO lt_findstring.

 

  REFRESH gt_founds.

  CALL FUNCTION 'RS_EU_CROSSREF'                            "#EC FB_RC

    EXPORTING

      i_find_obj_cls           = lc_obj_type

      no_dialog                = abap_true

    TABLES

      i_findstrings            = lt_findstring

      o_founds                 = gt_founds

    EXCEPTIONS

      not_executed             = 1

      not_found                = 2

      illegal_object           = 3

      no_cross_for_this_object = 4

      batch                    = 5

      batchjob_error           = 6

      wrong_type               = 7

      object_not_exist         = 8

      OTHERS                   = 9.

 

  IF gt_founds IS INITIAL.

    MESSAGE s398 WITH text-m01 space space space DISPLAY LIKE 'W'.  "Not found

  ENDIF.

 

ENDFORM.                    " SEARCH_RFC

 

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

*&      Form  DISPLAY_RESULTS

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

FORM display_results .

 

  DATA:

    lo_results       TYPE REF TO cl_salv_table,             "ALV

    lr_functions     TYPE REF TO cl_salv_functions_list,    "ALV Functions

    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

    lo_event_handler TYPE REF TO gcl_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.

 

  IF gt_founds IS NOT INITIAL.

    TRY.

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

        cl_salv_table=>factory( IMPORTING r_salv_table = lo_results CHANGING t_table = gt_founds ).

 

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

        lr_selections = lo_results->get_selections( ).

        lr_selections->set_selection_mode( if_salv_c_selection_mode=>single ).

 

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

        lr_display = lo_results->get_display_settings( ).

        lr_display->set_striped_pattern( if_salv_c_bool_sap=>true ).

 

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

        lr_functions = lo_results->get_functions( ).

        lr_functions->set_export_localfile( ).

        lr_functions->set_filter( ).

        lr_functions->set_print( ).

        lr_functions->set_sort_asc( ).

        lr_functions->set_sort_desc( ).

        lr_functions->set_find( ).

        lr_functions->set_detail( ).

 

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

        lr_columns = lo_results->get_columns( ).

        lr_columns->set_key_fixation( ).

        lr_columns->set_optimize( ).

        lt_column_ref = lr_columns->get( ).

 

        lr_columns->set_column_position( columnname = 'ENCL_OBJEC' position = 1 ).

        lr_columns->set_column_position( columnname = 'TEXTLINE'   position = 4 ).

 

        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=>left ).

          lr_column->set_visible( if_salv_c_bool_sap=>false ).

          lr_column->set_technical( if_salv_c_bool_sap=>true ).

 

          IF ls_column_ref-columnname = 'ENCL_OBJEC' OR ls_column_ref-columnname = 'OBJECT' OR

             ls_column_ref-columnname = 'PROGRAM'.

 

            CASE ls_column_ref-columnname.

              WHEN 'OBJECT'.  "Sub Object

                lr_column->set_long_text( text-a03 ).

                lr_column->set_medium_text( text-a03 ).

                lr_column->set_short_text( text-a03 ).

              WHEN 'PROGRAM'. "Rotine

                lr_column->set_long_text( text-c04 ).

                lr_column->set_medium_text( text-c04 ).

                lr_column->set_short_text( text-c04 ).

            ENDCASE.

 

            lr_column->set_key( if_salv_c_bool_sap=>true ).

            lr_column->set_visible( if_salv_c_bool_sap=>true ).

            lr_column->set_technical( if_salv_c_bool_sap=>false ).

          ENDIF.

 

          IF ls_column_ref-columnname = 'OBJECT_CLS'.

            lr_column->set_key( if_salv_c_bool_sap=>true ).

            lr_column->set_visible( if_salv_c_bool_sap=>true ).

            lr_column->set_alignment( if_salv_c_alignment=>centered ).

            lr_column->set_technical( if_salv_c_bool_sap=>false ).

          ENDIF.

 

          IF ls_column_ref-columnname = 'TEXTLINE'. "Description

            lr_column->set_long_text( text-a01 ).

            lr_column->set_medium_text( text-a01 ).

            lr_column->set_short_text( text-a02 ).

 

            lr_column->set_visible( if_salv_c_bool_sap=>true ).

            lr_column->set_alignment( if_salv_c_alignment=>left ).

            lr_column->set_technical( if_salv_c_bool_sap=>false ).

          ENDIF.

        ENDLOOP.

 

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

        lr_events = lo_results->get_event( ).

        CREATE OBJECT lo_event_handler.

        SET HANDLER lo_event_handler->on_double_click FOR lr_events.

 

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

        lo_results->display( ).

 

      CATCH cx_root.                                     "#EC CATCH_ALL

        MESSAGE s398 WITH text-m02 space space space DISPLAY LIKE 'E'.  "Error displaying result

    ENDTRY.

  ENDIF.

 

ENDFORM.                    " DISPLAY_RESULTS

 

 

Selection screen layout after implementation:

RFC.png

Result ALV layout:

RFC2.png

 

How to use:

ZNM_FIND_RFC is an IT tool to be used only for nonexistent remote function modules. Please use standard where-used functionality for all others situations.

  1. Just fill RFC name and execute;
  2. Check result objects and double click rows to navigate to objects source code where RFC is being used.

 

 

Nuno Morais

WebWork 2014


Hey SCN,

 

I was reading Bruno Esperança's post ( The last runtime buffer you'll ever need?) yesterday and it inspired me to think about the way I cache data in my own classes. I got to google-ing and found a nice blog about Caching with Decorator pattern and I thought I might give it a try in ABAP. I think the pattern works nicely for caching and as the author of Caching with Decorator pattern says:

I think this is a good way of applying caching, logging or any other things that you want to do before or after hitting your database.  It leaves your existing system in place and does not pollute your pure data access code (repositories) with other concerns.  In this case both classes have their own responsibilities, when it’s not in the cache the decorator class delegates the task to the repository and let it deal with the database.  Do I hear Single Responsibility Principal -


So lets jump right in to it. I just used the normal SAP example - SBOOK. For our fictitious program we just need to be able to select a single entry from SBOOK and we happen to know all the key fields.

 

I started with an interface:

INTERFACE ZIF_SBOOK_DB

PUBLIC.

METHODS:

     FIND_BY_KEY IMPORTING CARRID TYPE S_CARR_ID

                           CONNID TYPE S_CONN_ID

                           FLDATE TYPE S_DATE

                           BOOKID TYPE S_BOOK_ID

                 RETURNING VALUE(RS_SBOOK) TYPE SBOOK.

ENDINTERFACE.

 

Then I created the basic implementation - selecting from the database directly:

CLASS ZCL_SBOOK_DB_IMPL DEFINITION

PUBLIC

CREATE PUBLIC .

 

PUBLIC SECTION.

   INTERFACES: ZIF_SBOOK_DB.

PROTECTED SECTION.

PRIVATE SECTION.

ENDCLASS.

 

CLASS ZCL_SBOOK_DB_IMPL IMPLEMENTATION.

METHOD ZIF_SBOOK_DB~FIND_BY_KEY.

   SELECT SINGLE *

     INTO RS_SBOOK

     FROM SBOOK

     WHERE CARRID = CARRID

       AND CONNID = CONNID

       AND FLDATE = FLDATE

       AND BOOKID = BOOKID.

ENDMETHOD.

ENDCLASS.

 

Now we could just stop there... We have a perfectly good database layer and it meets the requirements of whatever fictitious program we are creating. Lets assume we have some performance problems, or maybe we just noticed in ST05 that the same query is being executed multiple times. This is where the decorator pattern comes in to play:

CLASS ZCL_SBOOK_DB_CACHE_DECORATOR DEFINITION

PUBLIC

FINAL

CREATE PUBLIC

INHERITING FROM ZCL_SBOOK_DB_IMPL.

 

PUBLIC SECTION.

   METHODS: ZIF_SBOOK_DB~FIND_BY_KEY REDEFINITION.

PROTECTED SECTION.

PRIVATE SECTION.

   DATA: _CACHE TYPE HASHED TABLE OF SBOOK WITH UNIQUE KEY CARRID CONNID FLDATE BOOKID.

ENDCLASS.

 

CLASS ZCL_SBOOK_DB_CACHE_DECORATOR IMPLEMENTATION.

METHOD ZIF_SBOOK_DB~FIND_BY_KEY.

   READ TABLE _CACHE INTO RS_SBOOK WITH KEY CARRID= CARRID CONNID = CONNID FLDATE = FLDATE BOOKID = BOOKID.

   IF SY-SUBRC NE 0.

     RS_SBOOK= SUPER->ZIF_SBOOK_DB~FIND_BY_KEY( CARRID = CARRID CONNID = CONNID FLDATE = FLDATE BOOKID = BOOKID ).

     INSERT RS_SBOOK INTO TABLE _CACHE.

   ENDIF.

ENDMETHOD.

ENDCLASS.

 

I think this is pretty easy to understand. We have defined a class that inherits from our basic implementation. It checks a private attribute (the cache) to see if it already has the item you need. If it doesn't have it, then it delegates to the super class - our basic implementation - and queries the database then puts the result in to the cache.

 

I see a couple of advantages in using the decorator pattern in this way to implement caching:

  • The buffering technique is not coupled to the implementation of the database layer. If I wanted to use shared memory objects instead of a private attribute that change would be easy to implement and I could be confident that it would not impact my existing database layer.
  • I can easily decide in any program I write whether or not I want to utilize the buffer. To buffer I instantiate an instance of zcl_sbook_db_cache_decorator and to ensure I always go directly to the database I instantiate an instance of zcl_sbook_db_impl.
  • I can add buffering to any existing database layer classes I may have already written without touching the existing (and proven!) code in those classes just by sub-classing them.

 

Finally, I decided I better test the performance. I was pretty confident that the cache would be faster, but I guess you never know:

REPORT Z_TEST_SBOOK_DB_LAYER.

 

DATA: T1 TYPE I,

     T2 TYPE I,

     TDIFF TYPE I.

 

DATA: LV_CARRID TYPE S_CARRID VALUE 'AA',

     LV_CONNID TYPE S_CONN_ID VALUE '17',

     LV_FLDATE TYPE S_DATE VALUE '20121031',

     LV_BOOKID TYPE S_BOOK_ID VALUE '23'.

 

DATA: LO_SBOOK_CACHE TYPE REF TO ZIF_SBOOK_DB.

CREATE OBJECT LO_SBOOK_CACHE TYPE ZCL_SBOOK_DB_CACHE_DECORATOR.

 

WRITE: / 'First read from the cache decorator will be from the database.'.

SET RUN TIME CLOCK RESOLUTION HIGH.

GET RUN TIME FIELD T1.

 

LO_SBOOK_CACHE->FIND_BY_KEY( CARRID = LV_CARRID

                            CONNID= LV_CONNID

                            FLDATE= LV_FLDATE

                            BOOKID= LV_BOOKID ).

 

GET RUN TIME FIELD T2.

TDIFF= ( T2 - T1 ).

WRITE: / 'It took ', TDIFF, ' microseconds to read from the database.'.

 

WRITE: / 'Second read from the cache decorator will be from the cache.'.

GET RUN TIME FIELD T1.

 

LO_SBOOK_CACHE->FIND_BY_KEY( CARRID = LV_CARRID

                            CONNID= LV_CONNID

                            FLDATE= LV_FLDATE

                            BOOKID= LV_BOOKID ).

 

GET RUN TIME FIELD T2.

TDIFF= ( T2 - T1 ).

WRITE: /'It took ', TDIFF, ' microseconds to read from the cache.'.

And here are the results.

results.PNG

 

So as you can see, it's a bit of an improvement I hope you find this useful in your own development!

Introduction

 

This blog has been inspired by Bruno Esperança and his thought provoking The last runtime buffer you'll ever need? The thing is, I wrote such a thing a few years ago, that's widely used by one of my clients. There's a few areas it could be improved

 

The Interface

 

interface zif_lookup

   public .

     constants c_dateto type fieldname value 'DATETO'. "#EC NOTEXT

 

   methods lookup

     exporting

       es_data type any

       eo_type type ref to cl_abap_structdescr

       e_notfound_flag type char1

     exceptions

       sql_error .


   methods set_key_val

     importing

       i_component type clike

       i_value type any .


   methods set_tim_val

     importing

       i_component type clike

       i_value type any .


   methods set_val_component

     importing

       i_component type clike .


   methods get_ref2_lookup

     returning

       value(rp_data) type ref to data

     exceptions

       sql_error .


   methods get_val_struc

     returning

       value(ro_valstruc) type ref to cl_abap_structdescr .


   methods get_notfound_flag

     returning

       value(r_notfound_flag) type flag .


endinterface.


Buffering in use


Well, the constructor is missing, so just showing you the interface doesn't really help! So, imagine a class with this attribute.

 

DATA: buffer TYPE REF TO zif_lookup.

 

Then we have a method in some class that reads materials from the MARC table, with i_matnr, i_werks, exporting e_bwtty and e_mmsta.

 

IF buffer IS NOT bound.

  CREATE OBJECT buffer TYPE zcl_table_lookup EXPORTING

     i_table_name = 'MARC'

     i_whole_tab = abap_false.

 

" The data I want to get back

  buffer->set_val_component( 'BWTTY' ).

  buffer->set_val_component( 'MMSTA' ).

ENDIF.

 

" The key data

buffer->set_key_val( i_component = 'MATNR' i_value = i_matnr )

buffer->set_key_val( i_component = 'WERKS' i_value = i_werks ).

 

" Now look it up.

DATA: BEGIN OF looked_up,

bwtty TYPE bwtty_d,

mmsta TYPE mmsta,

END OF looked up.

 

buffer->lookup( IMPORTING es_data = looked_up ).

e_bwtty = looked_up-bwtty.

e_mmsta = looed_up-mmsta.

 

What the class ZCL_TABLE_LOOKUP does is take the supplied components through set_val_component and set_key_val, and constructs two hashed tables (using RTTS) with the key components (in this case, MATNR and WERKS) as key. The first contains data looked up, the second contains data looked up and not found.

 

After the first lookup, you can't change the component fields.

 

If the i_whole_tab parameter is set, then the whole table will be buffered, rather than doing line by line buffering.

 

A couple of other implementations

 

I created a BW style look up, that uses the table look up above, and the same interface. It specialises into two further classes - one for looking up InfoObject master data, the other reading from a DSO.

 

Their constructors take an InfoObject name / DSO name, convert that to the underlying transparent tables, and instantiates a table lookup instance for this table. Oh - and for InfoObjects there's a flag on the constructor for whether the data is time dependent.

 

The component setting methods similarly take InfoObject names (instead of field names). The InfoObject names are then converted to field names, and passed to the table lookup instance.

 

A bit about internals

I'll concentrate on the TABLE buffer implementation, as it is that which is the engine.

 

SET_VAL_COMPONENT SET_KEY_VAL

There are two HASHED internal table, both with the same structure of fieldname, value and fieldtype (keyed on fieldname). The field type is simply gained via DD03L, using the table and fieldname.

 

The val_component (the fields which define which values will be returned) doesn't populate the value field. Values for the key fields are held as strings.

 

SET_TIM_VAL

 

This method is used for time dependent data.

 

LOOKUP

 

No buffer tables are created until the first time this method runs. Using RTTS, I build a few table types and references to these a few tables based on those types. The types of these variables, I'll leave as a exercise for the readers - they're not difficult to work out

 

* Add key fields

  LOOP AT me->th_key_fields ASSIGNING <ls_field>.

    ls_component-name = <ls_field>-fieldname.

    ls_component-type ?= cl_abap_tabledescr=>describe_by_name( <ls_field>-fieldtype ).

    APPEND ls_component TO lt_tab_component.

    APPEND ls_component TO lt_ntim_component.

    APPEND <ls_field>-fieldname TO lt_key.

  ENDLOOP.

 

 

* Time dependent fields

  IF me->s_tim_field IS NOT INITIAL.

    ls_component-name = me->s_tim_field-fieldname.

    ls_component-type ?= cl_abap_tabledescr=>describe_by_name( me->s_tim_field-fieldtype ).

    APPEND ls_component TO lt_tab_component.

    APPEND me->s_tim_field-fieldname TO lt_key.

    ls_component-name = c_index_fld.

    ls_component-type ?= cl_abap_tabledescr=>describe_by_data( sy-tabix ).

    APPEND ls_component TO lt_ntim_component.

  ENDIF.

 

 

* Add value fields for buffer

  LOOP AT me->th_val_fields ASSIGNING <ls_field>.

    ls_component-name = <ls_field>-fieldname.

    ls_component-type ?= cl_abap_tabledescr=>describe_by_name( <ls_field>-fieldtype ).

   READ TABLE lt_tab_component TRANSPORTING NO FIELDS WITH KEY name = ls_component-name.

    IF sy-subrc IS NOT INITIAL.

      APPEND ls_component TO lt_tab_component.

    ENDIF.

    APPEND ls_component TO lt_val_component.

  ENDLOOP.

 

 

* Create table structure and types

  lo_tablestruc = cl_abap_structdescr=>create( lt_tab_component ).

  lo_hash_table_type =

           cl_abap_tabledescr=>create( p_line_type  = lo_tablestruc

                                       p_table_kind = cl_abap_tabledescr=>tablekind_hashed

                                       p_unique     = abap_true

                                       p_key        = lt_key ).

  lo_sort_table_type =

           cl_abap_tabledescr=>create( p_line_type  = lo_tablestruc

                                       p_table_kind = cl_abap_tabledescr=>tablekind_sorted

                                       p_unique     = abap_true

                                       p_key        = lt_key ).

 

* Create "time key" structure and table

  IF me->s_tim_field IS NOT INITIAL.

    DELETE lt_key WHERE name EQ me->s_tim_field-fieldname.

 

 

    lo_ntim_struc = cl_abap_structdescr=>create( lt_ntim_component ).

    lo_ntim_table_type =

             cl_abap_tabledescr=>create( p_line_type = lo_ntim_struc

                                         p_table_kind = cl_abap_tabledescr=>tablekind_hashed

                                         p_unique     = abap_true

                                         p_key        = lt_key ).

 

    CREATE DATA me->ps_ntim_lookup  TYPE HANDLE lo_ntim_struc.

    CREATE DATA me->pth_ntim_lookup TYPE HANDLE lo_ntim_table_type.

ENDIF.

 

* Create value structure types

  me->o_valstruc  = cl_abap_structdescr=>create( lt_val_component ).

 

 

* Create pointers to the lookup table, workarea and values

  CREATE DATA me->pth_lookup    TYPE HANDLE lo_hash_table_type.   " The main buffer

CREATE DATA me->ps_lookup     TYPE HANDLE lo_tablestruc.             " The key fields in a structure

CREATE DATA me->ps_val        TYPE HANDLE me->o_valstruc.            " The values being read, in a structure

  CREATE DATA me->pth_lookup_nf TYPE HANDLE lo_hash_table_type.  " The buffered "not found" values

 

Then we need to read the data. For a whole table buffer, it's a simple piece of dynamic SQL. Note how pool tables are handled. It doesn't actually matter if we get duplicates selected; it's just less efficient.

 

     TRY.

          SELECT DISTINCT (me->t_select) FROM (me->table) INTO TABLE <lt_lookup>.

        CATCH cx_sy_dynamic_osql_error cx_sy_open_sql_db INTO lx_error.

         " Handle the fact that you can't use DISTINCT with pool tables

          TRY.

              SELECT (me->t_select) FROM (me->table) INTO TABLE <lt_lookup>.

            CATCH cx_sy_dynamic_osql_error cx_sy_open_sql_db INTO lx_error.

              RAISE sql_error. "<---- this really should be a proper class based exception. I'm sorry...

          ENDTRY.

      ENDTRY.

 

Now, we can actually get data. <ls_lookup> is constructed from ps_lookup, and contains the key values.

 

* See if the value is already in the table

  READ TABLE <lth_lookup> INTO <ls_lookup> FROM <ls_lookup>.

 

If it's there, we're done. <ls_lookup> with contain the key fields and value fields. Just pass the value fields back to the calling program. If it isn't there we have to check the <lth_lookup_nf> table (ok, for whole table buffering we don't need to; we know it's not found if it isn't in <lth_lookup>. With line by line buffering, we have to do a single dynamic SQL read. Here, the time dependent reads may come into play.

 

lt_where = me->get_where_tab( ).

TRY.

   SELECT (me->t_select) FROM (me->table) INTO xs_lookup WHERE (lt_where) ORDER BY (me->orderby). " Orderby is only set for time dependence

     IF not time dependent OR date GT date in the key.

        EXIT.

      ENDIF.

   ENDSELECT.

CATCH cx_dy_dynamic_osql_error cx_sy_open_sql_db.

   raise SQL_ERROR.

ENDTRY.

 

If the data isn't found, then it's added to the not found table, otherwise it's added to the found table.

 

Simple, isn't it?

 

Improvements

  • With the table name, we can easily get the key fields from DD03L. We still need set_key_val, or we don't know the values to select against.
  • Put in proper exception handling. It's pretty dire. You can tell I built this before I understood exception classes well . But I do now, honest!

Hi SCN community!

 

It's me again, with another contribution to Project Object.

 

Has it ever happened to you to be in a situation where you might be requesting the same thing over and over again to the database?

 

And if you're a good developer, you avoided repetitive calls to the database implementing a buffer, correct?

 

Well, what I've got for you today is a class the will serve as a buffer for everything you want! Everything? Everything!

 

 

I can't take full credits for this though... I got this from a guy who got this from another guy... so I have no idea who was the actual developer of this thing. I can take the credit for "perfecting" it though, and implementing some exception classes in it. So at least that

 

You'll be able to find it in nugget and text version in my github, in the utilities section:

GitHub

 

 

Use example

 

Below is just an example of how to use this class. I am fully aware that the first "loop" is not how someone would properly perform this particular select to the database, this is meant simply as an example of how to use this class and for what.

 

 

 

DATA:
      db_counter TYPE i,
      lt_sbook  TYPE TABLE OF sbook,
      ls_sbook  LIKE LINE OF lt_sbook,
      ls_sbuspart TYPE sbuspart.

SELECT * FROM sbook
  INTO TABLE lt_sbook.

BREAK-POINT.

CLEAR db_counter.

LOOP AT lt_sbook INTO ls_sbook.

  SELECT SINGLE * FROM sbuspart
    INTO ls_sbuspart
    WHERE buspartnum = ls_sbook-customid.
  ADD 1 TO db_counter.

ENDLOOP.

"check db_counter
BREAK-POINT.

CLEAR db_counter.

LOOP AT lt_sbook INTO ls_sbook.

  TRY.

      CALL METHOD zcl_buffer=>get_value
        EXPORTING
          i_name = 'CUSTOMER_DETAILS'
          i_key  = ls_sbook-customid.
    CATCH zcx_buffer_value_not_found.

      "If we haven't saved it yet, get it and save it

      SELECT SINGLE * FROM sbuspart
        INTO ls_sbuspart
        WHERE buspartnum = ls_sbook-customid.
      ADD 1 TO db_counter.

      CALL METHOD zcl_buffer=>save_value
        EXPORTING
          i_name  = 'CUSTOMER_DETAILS'
          i_key   = ls_sbook-customid
          i_value = ls_sbuspart.

  ENDTRY.

ENDLOOP.

"check db_counter
BREAK-POINT.

 

Performance remark

 

One last remark that I should make though... due to the high flexibility of this buffer, I think it's not possible to have a sorted read (or, in other words, a fast read) of the value in the buffer. Therefore, if you are using a buffer with a high volume of entries, and if performance is critical, you should create a subclass and redefine the "key" with the type you are interested in particular, and also redefine the get method to replace the "LOOP" statement with a "READ" statement.

 

All the best!

Bruno

Hi there.

As it was shown previously there are some limitations on usage of RFC enabled forms. Most of it is easily avoidable, but the main limitation is that it's impossible to pass references into such forms. Currently import/export routines doesn't allow to pass references, so here is the problem.

An option to bypass such limitation is to serialize objects into some string container. Lucky us, SAP developed transformation called "id" that could handle everything. But, at the same time, such transformation creates an XML, so there is a big overhead. But, it could be handled as well, as SAP developed kernel-based methods for compression of strings. In this test length of the original XML is 586 bytes, and length of compressed one is 302. Pretty impressive, huh ?

Only thing that should be highlighted is that class used for serialization must implement interface IF_SERIALIZABLE_OBJECT. Actually this interface doesn't declare any methods, so you have to put it in interfaces section. That's it !

 

Another addition : this method is works pretty well if object contains nested objects, after deserialization objects would be recreated and references would be set properly. So, for example this method would work well for linked links. There is no example here, to keep things simple, but it works.

 

So, here is the code (there is no exceptions handling for the simplicity, but in productive software you have to always handle such exceptions):

REPORT Z_BINARY_TRANSFORM3.
class z_ser_data definition.
  PUBLIC SECTION.
    INTERFACES: IF_SERIALIZABLE_OBJECT.
    data: member type string read-only.
  METHODS:
    constructor
      IMPORTING
        in_val type string,
    get_val
      RETURNING VALUE(out_val) type string,
    change_val
      IMPORTING
        new_val type string.
endclass.
class z_ser_data implementation.
  METHOD constructor.
    member = in_val.
  ENDMETHOD.
  method get_val.
    out_val = me->member.
  endmethod.
  method change_val.
    me->member = new_val.
  endmethod.
endclass.
data: lr_class type ref to z_ser_data,
      lv_ser_xml type string,
      lv_x_gzip  type xstring.
FIELD-SYMBOLS: <fs> type any.
INITIALIZATION.
create object lr_class
  exporting
      in_val = 'String from calling program'.
CALL TRANSFORMATION id
  SOURCE model = lr_class
  RESULT XML lv_ser_xml.
CL_ABAP_GZIP=>compress_text(
  exporting
    text_in        = lv_ser_xml    " Input Text
*    text_in_len    = -1    " Input Length
    compress_level = 9    " Level of Compression
*    conversion     = 'DEFAULT'    " Conversion to UTF8 (UC)
  importing
    gzip_out       = lv_x_gzip     " Compressed output
*    gzip_out_len   =     " Output Length
).
*  catch cx_parameter_invalid_range.    " Parameter with Invalid Range
*  catch cx_sy_buffer_overflow.    " System Exception: Buffer too Short
*  catch cx_sy_conversion_codepage.    " System Exception Converting Character Set
*  catch cx_sy_compression_error.    " System Exception: Compression Error
perform describe_buffer using lv_x_gzip.
form describe_buffer
  using in_buffer type xstring.
  data: lr_another_obj type ref to z_ser_data,
        lv_str type string.
  CL_ABAP_GZIP=>decompress_text(
    exporting
      gzip_in      = in_buffer    " Input of Zipped Data
*      gzip_in_len  = -1    " Input Length
*      conversion   = 'DEFAULT'    " Conversion to UTF8 (UC)
    importing
      text_out     = lv_str    " Decompessed Output
*      text_out_len =     " Output Length
  ).
*    catch cx_parameter_invalid_range.    " Parameter with Invalid Range
*    catch cx_sy_buffer_overflow.    " System Exception: Buffer too Short
*    catch cx_sy_conversion_codepage.    " System Exception Converting Character Set
*    catch cx_sy_compression_error.    " System Exception: Compression Error
  CALL TRANSFORMATION id
    SOURCE XML lv_str
    RESULT model = lr_another_obj.
  write: 'Received val:', lr_another_obj->member.
  NEW-LINE.
  lr_another_obj->change_val( new_val = 'New val' ).
  write: 'Changed val:', lr_another_obj->member.
  new-line.
endform.


Hi there.

As it was shown in previous example it's possible to supply any data into RFC enabled FORM. But, at the same time direct usage of IMPORT clause requires explicit specification of the ID's in data buffer and also involves some manual work to define types. Fortunately there is a special class CL_ABAP_IMPEXP_UTILITIES that could handle all dirty work.

The only limitation I've found - it works pretty good with DDIC types, but for custom defined types it could fail without any detailed explanation.

This greatly simplifies extension of the methods and makes code much more compact and readable.

I've tested this example with 50000 lines and seems ABAP can handle such amount of data. Generally for the parallel processing this should be enough.

 

So, here is the sample code:

REPORT Z_BINARY_TRANSFORM2.
data: x_b2      type xstring,
      lt_abc    type STANDARD TABLE OF t000,
      wa_t000  type t000.
INITIALIZATION.
* Fill table with sample data
wa_t000-mandt = sy-mandt.
wa_t000-mtext = 'SCN Demo #1'.
wa_t000-ort01 = 'Moscow'.
append wa_t000 to lt_abc.
wa_t000-mandt = sy-mandt.
wa_t000-mtext = 'SCN Demo #2'.
wa_t000-ort01 = 'Tokio'.
append wa_t000 to lt_abc.
* Export data into buffer
export tadir = lt_abc to DATA BUFFER x_b2 COMPRESSION on.
* Check the result
perform describe_buffer using x_b2.
form describe_buffer
  using in_buffer type xstring.
  data: lt_datatab type tab_cpar.
  FIELD-SYMBOLS: <fs> like line of lt_datatab,
                <fs2> type any table,
                <fs3> type any.
  lt_datatab = cl_abap_expimp_utilities=>dbuf_import_create_data( dbuf = in_buffer ).
  loop at lt_datatab assigning <fs>.
        write : <fs>-name.    " Here is the name of the currently processed tab in buffer
        new-line.
        assign <fs>-dref->* to <fs2>.
        loop at <fs2> assigning <fs3>.
                  write : <fs3>.
                  new-line.
        endloop.
  endloop.
endform.

Hi there.

As data growth is constantly increasing handling such amount of data requires more and more time. Most logical way of solving this issue is to handle data in parallel. Currently for parallel processing ABAP offers only one way - RFC enabled functional modules. This approach is quite old and mostly known. But ... there are some limitations exist in RFC FM's. One of it : it doesn't allow to pass references. Usually it's not a problem, but in my case tool is working with multiple data structures, that usually stored as ref to data.

It's impossible to pass such data directly in RFC FM, so here is the trick:

  1. Assign variable with ref to data to field symbol
  2. Export this field symbol to buffer as XString
  3. Pass xstring to RFC FM
  4. Perform backward transformation

 

There are two small tricks with import/export: first one is that you have to explicitly name objects that you export, and during import use same names. Even if data buffer contains data for exactly one object. Other thing is the compression : in this small test without compression data buffer have length 190 bytes, and with compression just 95.

Documentation says that this export routine could fail in case of out of memory, but current limits are not described.

 

Here is the simple example how it works:

REPORT Z_BINARY_TRANSFORM.
data: lr_data type ref to data,
      lt_test type table of string,
      wa_string type string,
      x_buffer  type xstring.
FIELD-SYMBOLS: <fs1> type any,
               <fs2> type any.
INITIALIZATION.
** Fill source table with test data
DO 5 TIMES.
  wa_string = sy-index.
  CONDENSE wa_string.
  CONCATENATE 'test' wa_string  into wa_string SEPARATED BY '_'.
  append wa_string to lt_test.
ENDDO.
create data lr_data like lt_test.
assign lt_test to <fs1>.
assign lr_data->* to <fs2>.
<fs2> = <fs1>.
export rep_tab = <fs2> to data buffer x_buffer compression on.
PERFORM abc using x_buffer.
form abc
  using in_buffer type xstring.
  data: lr_data2 type ref to data,
        lt_test2 type table of string.
  field-symbols: <fs3> type standard table,
                 <fs4> type any.
  create data lr_data2 type table of string.
  assign lr_data2->* to <fs3>.
*  append 'test' to <fs3>.
  import rep_tab = <fs3> from data buffer in_buffer.  " Import must be performed on the same variable name that was used for export
****  Output supplied table
  LOOP AT <fs3> ASSIGNING <fs4>.
    write: <fs4>.
    NEW-LINE.
  ENDLOOP.
ENDFORM.

Speaking about the number ranges, I am trying to give a small write up where in I am presenting the best practice scenario for number ranges.

 

Old Numbers Vs New Numbers

Moving from Legacy system to New environment presents an opportunity to clean up the data.
It is very common that there will be duplicate entries in the legacy system and some master data that’s outdated.

With the new system, we will have an opportunity to get rid of the data that’s of no use for business. This also
reduces data maintenance costs.

 

If we go for Old Numbers (using legacy numbers in the new system also) and if there were
duplicates, it’s possible that there would be gaps in the numbers.

 

Because of the above reasons, Businesses go for new number ranges. Unless there is a specific
‘business reason’ it is strongly advised to go for new numbers.

Who decides Number Ranges (Internal or External)?

 

Master Data: There is some Master Data for which normally businesses go for external numbers – for
example, Finished Products – where number of records is very small (in a few thousands).

Businesses may want to ‘construct’ their numbers based on a particular criteria – for example,

Finished Products start with ‘1’ followed by Plant ‘0002’ then followed by Product Group ‘01’ and then a 5 digit number
10001 – this will give us number 100020110001. Users would be able to find very
easily what this product is and where it is produced etc., very easily.

 

Note:- Even here, every number has to be ‘constructed’ meticulously and it takes lot of efforts.

               
There is some Master Data for which normally businesses go for internal numbers – for

example, Customers or Raw materials – where number of records is very high (in few hundred thousands).

So, Sold-to customers could be from 1000000000 to 1999999999. It does not make sense to have

external number ranges here because it will break the backs of business team members!

 

Transaction Data: In general Transaction Data is always a candidate for ‘Internal Numbers’. I have
never seen any engagement going for external numbers here.


Points:

There will be high resistance from Business Users if existing numbers are going to be changed –
they already memorized everything and they (even we do) hate to lose association with old pals.

         We should present them following things here:

  • How the duplicates effect the gaps in numbers
  • How the SAP system STILL allows them to use old numbers to search new numbers.
  • Involve them in ‘constructing’ the number ranges.


     2. Let the system ‘manage’ numbers – it’s better to let system do the work for us rather than involving 100’s of users

 

While doing Data Migration, we follow a different method.We ask functional people to make every number range as ‘external’ and

then let ETL tool generate the numbers and load the data.

Once the data is loaded, all the number ranges are turned back to their original status.

 

 

 

 

 

 













Actions

Filter Blog

By author:
By date:
By tag: