March 25, 2015

Dynamic code generation in ABAP

Recently I discovered an excellent feature in ABAP which has helped me a lot in a requirement I had. This feature is the capacity to generate dynamic code and execute it in your program. As far as I know is possible to generate forms, classes and even programs.

My requirement needed to basically copy an SQL to create a special output. Following the DRY principle I didn't want to duplicate code. I wanted to use a parameter to change a portion of the SQL and get a dataset.

After some research I found this excellent feature which allows me to use a common procedure to manage any table and handle that data. 

Let's code an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
report  zdynamic.

tables mara.

data: gt_output type standard table of mara,
      wa_output like line of gt_output,
      gt_output2 type standard table of marc,
      wa_output2 like line of gt_output2.

select-options so_matnr for mara-matnr.

start-of-selection.

  write / 'MARA'. " Generate output for table 'MARA'
  perform create_dataset tables gt_output so_matnr using 'mara'.
  loop at gt_output into wa_output.
    write: / wa_output-matnr, wa_output-meins.
  endloop.

  write: /, /, 'MARC'. " Generate output for table 'MARC'
  perform create_dataset tables gt_output2 so_matnr using 'marc'.
  loop at gt_output2 into wa_output2.
    write: / wa_output2-matnr, wa_output2-maabc.
  endloop.

*&---------------------------------------------------------------------*
*&      Form  create_dataset
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
*      -->P_OUTPUT   Result table
*      -->P_SELECT   Search parameter
*      -->P_TABLE    Table name
*----------------------------------------------------------------------*
form create_dataset tables p_output p_select using p_table type string.
  data: it_source type standard table of string with header line, " Structure that will hold the program to be generated
        lv_program type string, " The name of the program
        lv_error type string.  " The error in generation

  it_source = 'report create_dataset.'.
  append it_source.
  it_source = 'form execute tables p_output s_matnr.' .
  append it_source.
  it_source = 'select * from '.
  append it_source.
  it_source = p_table.
  append it_source.
  it_source = ' up to 10 rows into corresponding fields of '.
  append it_source.
  it_source = ' table p_output'.
  append it_source.
  it_source = 'where matnr in s_matnr.'.
  append it_source .
  it_source = 'endform.'.
  append it_source.

  generate subroutine pool it_source
           name lv_program
           message lv_error. " Generate the program. Here is where magic happens.

  if sy-subrc eq 0.
    write: / 'Program name:', lv_program.
    perform execute in program (lv_program) tables p_output p_select if found.
  else.
    write: / 'Error ocurred', lv_error.
    write sy-subrc.
  endif.
  write /.
endform.                    "create_dataset

The objective here is to read data from different tables. We will send a table name as a parameter to a form and generate ABAP code to execute it.

The form 'create_dataset' is the place where the generation happens. As we debug the code you can see that the internal table 'it_source' has the generated source code that is constructed using the parameter 'p_table'.



After that, the source code is compiled and a program is generated using the following statement:

1
2
3
generate subroutine pool it_source
         name lv_program
         message lv_error.

If everything works the following data is produced:

LV_PROGRAM = "%_T00O4R" (This name changes when generated)
LV_ERROR = Blank if compilation is succesful.


As I wanted to print different outputs with different data I just send the table name as the parameter and let SAP construct the subroutine for me.

What happens if the code is wrong and there are compilation errors? The answer is that nothing happens, there will be no dump, at least not at this stage. When an error happens in the code you will get the description of that exception in the 'message' parameter and SY-SUBRC will have the value '4' or '8'.

According to SAP documentation, SY-SUBRC will have one of the following values:

0 Generation was successful.
4 Source code contains a syntax error.
8 A generation error occurred.

The last part is check the value of SY-SUBRC and call the generated program, sending  the parameters as defined in the code.

1
perform execute in program (lv_program) tables p_output p_select using p_table if found.

Remember that the parameters must match with the form otherwise you'll get a dump.

The output of the program is the following:


In summary this great feature helped me a lot and the most important part of this is that it let me save code. Code that you don't write is code that you don't need to debug.

For better reference on this issue check this link.

See you in the next. Hope it helps.

March 20, 2015

Get reservations in ABAP

Once I needed to make a report to present material reservations and after checking some tables, finally I found a series of BAPIs that helped me in this requirement. You can check the results of the code in transaction 'MMBE' with any material.

Let's code the program.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
report  zreservation.

data wa_detail type bapi_mrp_stock_detail.

data: begin of xtab1 occurs 10,
         werks like resb-werks,
         lgort like resb-lgort,
         charg like resb-charg,
         matnr like resb-matnr,
         bdmng like resb-bdmng,
         bdmns like resb-bdmng,
         erfme like resb-erfme,
         erfmg like resb-erfmg,
       end of xtab1.

data: arbed like resb-bdart  value 'AR',
      mtres like resb-bdart  value 'MR'.

ranges: xbdart   for resb-bdart,
        ms_werks for marcv-werks.

parameters: p_matnr type marc-matnr obligatory,
            p_werks type marc-werks obligatory.

start-of-selection.

  CALL FUNCTION 'BAPI_MATERIAL_STOCK_REQ_LIST'
    EXPORTING
      MATERIAL                = p_matnr
      PLANT                   = p_werks
*     MRP_AREA                =
*     PLAN_SCENARIO           =
*     SELECTION_RULE          =
*     DISPLAY_FILTER          =
*     PERIOD_INDICATOR        =
*     GET_ITEM_DETAILS        =
*     GET_IND_LINES           = 'X'
*     GET_TOTAL_LINES         =
*     IGNORE_BUFFER           =
*     MATERIAL_EVG            =
    IMPORTING
*     MRP_LIST                =
*     MRP_CONTROL_PARAM       =
      MRP_STOCK_DETAIL        = wa_detail
*     RETURN                  =
*   TABLES
*     MRP_ITEMS               =
*     MRP_IND_LINES           =
*     MRP_TOTAL_LINES         =
*     EXTENSIONOUT            =
            .

  refresh xbdart.
  clear   xbdart.
  xbdart-sign   = 'I'.
  xbdart-option = 'EQ'.
  xbdart-low    = arbed.
  append xbdart.
  xbdart-low    = mtres.
  append xbdart.

  refresh ms_werks.
  clear   ms_werks.
  ms_werks-sign   = 'I'.
  ms_werks-option = 'EQ'.
  ms_werks-low    = p_werks.
  append ms_werks.

  CALL FUNCTION 'MB_ADD_RESERVATION_QUANTITIES'
    EXPORTING
      X_KZEAR       = space
      X_MATNR       = p_matnr
      X_XLOEK       = space
*     X_KZWSO       = ' '
    TABLES
      XBDART        = xbdart
      XTAB1         = xtab1
      XWERKS        = ms_werks.

  write: 'Material:', p_matnr, 'Reservations:', wa_detail-reservations, /.
  loop at xtab1.
    write: 'Storage Location:', xtab1-lgort, 'Reservations:', xtab1-bdmng, /.
  endloop.

See you in the next post.

Hope it helps

March 6, 2015

Table maintenance and other features

In the previous post I showed how to make use of some of the features that SAP provides when making tables.

We made a table, we generated a table maintenance screen, we selected events and put come code there to show how events work. This time I would like to show another feature that we could use when we program events.

Ok, let's code;

First of all, let’s add another field and call it 'AGE' with the data element 'INT2'. Check the screen.


2. Activate the table

3. Update the dynpro to include the new field.
4. Go to Utilities / Table Maintenance Generator menu.


5. Go to Environment / Modification / Events menu. Click 'Ok' on the message box.


6. Add a new entry, select option '01' (Before saving the data in the database) and type 'ON_BEFORE_SAVE'

7. Click on the editor button.


8. Select the include previously created or create a new one. I'll use the one that exists.


9. Enter the following code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
form on_before_save.
  data wa_testtab type ztesttab.

  loop at total. " Check all the records
    if <vim_total_struc> is assigned.
      move-corresponding <vim_total_struc> to wa_testtab. " Get the line to check
    endif.

    if <action> ne 'D' and <action> ne 'X'. " Consider only create and update operations
      if wa_testtab-age not between 15 and 130.
        message 'Age is not valid. Must be between 1 and 130' type 'I' display like 'E'.
        vim_abort_saving = 'X'. " Abort operation
      endif.
    endif.
  endloop.
endform.

The variable 'total' is a table with header line which contains all the data of the table. 

The field symbol '<vim_total_struc>' is a structure that contains the line or tuple we are reading. Remember that at this point we haven't saved but we get the changes we've made to the record. Here we transfer the data from the field symbol to the work area of type 'WA_TESTTAB'.

The field symbol '<action>' indicates the action over the record. It can be 'I' (insert), 'U' (update), 'D' (delete) or 'X' (none). In this example we are going to validate when inserting or updating data.

The variable 'vim_abort_saving' is used to indicate if we cancel the operation. If the age is not between 15 and 130 we'll issue an error and SAP will not save the new entries or changes.

10. Activate the code.


11. Return and save.


12. Create a new entry without age and then try to save. The error 'Age is not valid. Must be between 15 and 130' will arise. No matter what you do, unless you type a valid age the data won't be saved. 



As you can see, you can make use of this feature to add more validations or manage your data in your table. 

Well, that's it for now. Hope it helps.