Customizing SAP for Dummies: ABAP Tutorial for Students with some Programming Experience – Part 3

August 20, 2008

It is time to continue the ABAP tutorial. After the basics in Part 1 and the first report in Part 2, in this part we are going to take a look at the different events in ABAP used in what is called an Interactive Report. An Interactive Report is supposed to display information about a list of items, and each time we click on one of these items, to display the details of such item in a so called subscreen.

As always let’s start with the source code of the report and the source code of the different FMs:

*&------------------------------------------------*
*& Report  Z_TEST_INT_REP
*&
*&------------------------------------------------*
*&
*&
*&------------------------------------------------*

REPORT  z_test_int_rep NO STANDARD PAGE HEADING.

DATA: vbak_input TYPE zvbak_input,
      vbak_output TYPE STANDARD TABLE OF ztabfm01,
      wa_vbak TYPE ztabfm01,

      vbap_input TYPE STANDARD TABLE OF ztabfm02,
      vbap_output TYPE STANDARD TABLE OF ztabfm02,
      wa_vbap TYPE ztabfm02,
      vbeln_n TYPE n LENGTH 10,

      vbep_output TYPE STANDARD TABLE OF ztabfm03,
      wa_vbep TYPE ztabfm03,

      box TYPE c.

PARAMETERS: vbtyp LIKE vbak_input-vbtyp
            OBLIGATORY VALUE CHECK,
            auart LIKE vbak_input-auart
            OBLIGATORY VALUE CHECK,
            vkorg LIKE vbak_input-vkorg
            OBLIGATORY VALUE CHECK,
            vtweg LIKE vbak_input-vtweg
            OBLIGATORY VALUE CHECK,
            spart LIKE vbak_input-spart
            OBLIGATORY VALUE CHECK.

INITIALIZATION.
  vbtyp = 'C'.
  auart = 'TA'.
  vkorg = '1000'.
  vtweg = '10'.
  spart = '00'.

TOP-OF-PAGE.
  WRITE:/ 'Sales Header'.
  ULINE.
  WRITE:/ 'Document', (10) 'Category',
        (12) 'Organization'.
  ULINE.

TOP-OF-PAGE DURING LINE-SELECTION.
  CASE sy-lsind.
    WHEN 1.
      WRITE:/ 'Sales Item'.
      ULINE.
      WRITE:/(15) 'Document', (10) 'Item',
      (17) 'Material Number', (12) 'Price'.
      ULINE.
    WHEN 2.
      WRITE:/ 'Sales Schedule Line'.
      ULINE.
      WRITE:/(15) 'Document', (10) 'Item',
      (18) 'Schedule Number', (15) 'Order quantity',
      (15) 'Conf. quantity'.
      ULINE.
  ENDCASE.

AT SELECTION-SCREEN.
  PERFORM vbak_in_to_out.
  IF vbak_output IS INITIAL.
    MESSAGE e888(sabapdocu) WITH 'No entries found'.
  ENDIF.

START-OF-SELECTION.
  PERFORM vbak_in_to_out.
  PERFORM display_data_vbak.

AT LINE-SELECTION.
  CASE sy-lsind.
    WHEN 1.
      CLEAR: vbap_input.
      DO.
        CLEAR: wa_vbap.
        READ LINE sy-index FIELD VALUE box
        wa_vbak-vbeln INTO vbeln_n.
        IF sy-subrc NE 0.
          EXIT.
        ELSEIF box = 'X'.
          MOVE vbeln_n TO wa_vbap-vbeln.
          APPEND wa_vbap TO vbap_input.
        ENDIF.
      ENDDO.

      IF vbap_input IS INITIAL.
        MESSAGE s888(sabapdocu) WITH
        'No line selected'.
        sy-lsind = sy-lsind - 1.
      ELSE.
        CLEAR vbap_output.
        CALL FUNCTION 'Z_TEST_FUNCT_BE_INT2'
          TABLES
            vbeln_in = vbap_input
            vbap_det = vbap_output.
        IF vbap_output IS INITIAL.
          MESSAGE s888(sabapdocu) WITH
          'No items to display'.
          sy-lsind = sy-lsind - 1.
        ELSE.
          PERFORM display_data_vbap.
        ENDIF.
      ENDIF.

    WHEN 2.
      CALL FUNCTION 'Z_TEST_FUNCT_BE_INT3'
        EXPORTING
          vbeln_in = wa_vbap-vbeln
          posnr_in = wa_vbap-posnr
        TABLES
          vbep_det = vbep_output.
      PERFORM display_data_vbep.

    WHEN 3.
      MESSAGE s888(sabapdocu)
      WITH 'No more subscreens available'.
  ENDCASE.

*&------------------------------------------------*
*&      Form  display_data_vbak
*&------------------------------------------------*
*       text
*&------------------------------------------------*
FORM display_data_vbak.
  LOOP AT vbak_output INTO wa_vbak.
    WRITE:/ box AS CHECKBOX, wa_vbak-vbeln,
    (10) wa_vbak-vbtyp, (12) wa_vbak-vkorg.
  ENDLOOP.
ENDFORM. "display_data_vbak

*&------------------------------------------------*
*&      Form  display_data_vbap
*&------------------------------------------------*
*       text
*&------------------------------------------------*
FORM display_data_vbap.
  LOOP AT vbap_output INTO wa_vbap.
    WRITE:/(15) wa_vbap-vbeln, (10) wa_vbap-posnr,
    (17) wa_vbap-matnr, (12) wa_vbap-netpr.
    HIDE wa_vbap.
  ENDLOOP.
ENDFORM. "display_data_vbap

*&------------------------------------------------*
*&      Form  display_data_vbep
*&------------------------------------------------*
*       text
*&------------------------------------------------*
FORM display_data_vbep.
  LOOP AT vbep_output INTO wa_vbep.
    WRITE:/(15) wa_vbep-vbeln, (10) wa_vbep-posnr,
    (17) wa_vbep-etenr, (15) wa_vbep-wmeng,
    (15) wa_vbep-bmeng.
  ENDLOOP.
ENDFORM. "display_data_vbep

*&------------------------------------------------*
*&      Form  vbak_in_to_out
*&------------------------------------------------*
*       text
*&------------------------------------------------*
FORM vbak_in_to_out.
  CLEAR: vbak_input, vbak_output.
  vbak_input-vbtyp = vbtyp.
  vbak_input-auart = auart.
  vbak_input-vkorg = vkorg.
  vbak_input-vtweg = vtweg.
  vbak_input-spart = spart.

  CALL FUNCTION 'Z_TEST_FUNCT_BE_INT1'
    EXPORTING
      vbak_input = vbak_input
    TABLES
      vbak_det   = vbak_output.
ENDFORM.                    "vbak_in_to_out
FUNCTION Z_TEST_FUNCT_BE_INT1.
*"------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(VBAK_INPUT) TYPE  ZVBAK_INPUT
*"  TABLES
*"      VBAK_DET STRUCTURE  ZTABFM01
*"------------------------------------------------

  SELECT vbeln erdat erzet ernam vbtyp
    auart vkorg vtweg spart FROM vbak
    INTO CORRESPONDING FIELDS OF TABLE vbak_det
*    UP TO 100 ROWS
    WHERE vbtyp = vbak_input-vbtyp AND
    auart = vbak_input-auart AND
    vkorg = vbak_input-vkorg AND
    vtweg = vbak_input-vtweg AND
    spart = vbak_input-spart.

ENDFUNCTION.
FUNCTION z_test_funct_be_int2.
*"------------------------------------------------
*"*"Local Interface:
*"  TABLES
*"      VBELN_IN STRUCTURE  ZTABFM02
*"      VBAP_DET STRUCTURE  ZTABFM02
*"------------------------------------------------

  SELECT vbeln posnr matnr kwmeng netpr werks
   INTO CORRESPONDING FIELDS OF TABLE vbap_det
   FROM vbap
   FOR ALL ENTRIES IN vbeln_in
   WHERE vbeln EQ vbeln_in-vbeln.

ENDFUNCTION.
FUNCTION z_test_funct_be_int3.
*"------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(VBELN_IN) TYPE  VBELN_VA
*"     REFERENCE(POSNR_IN) TYPE  POSNR
*"  TABLES
*"      VBEP_DET STRUCTURE  ZTABFM03
*"------------------------------------------------

  SELECT vbeln posnr etenr edatu wmeng bmeng
    FROM vbep
    INTO CORRESPONDING FIELDS OF TABLE vbep_det
    WHERE vbeln = vbeln_in AND posnr = posnr_in.

ENDFUNCTION.

The way this report works is the following:

When you run the report you are presented with a screen, called Selection Screen, where you can select the kind of Sales you want to display information about.

Selection Screen

Selection Screen

After choosing the kind and clicking continue (the green tick button on the upper left) you are presented with the sales headers, corresponding to what you have previously chosen.

First Display

First Display

After you have chosen the headers and clicked on the magnifying glass, the details of the chosen headers will be displayed, i.e. the sales items belonging to the chosen headers.

Second Display

Second Display

Finally, after double clicking any sales item, you will get information about the different schedules for the item.

Third and last display

Third and last display

As you can see, there are many new things in this example. I am just going to go through it and try to explain them all.

First of all, I am not declaring the types in the report anymore. I declare the types in the ABAP Data Dictionary. Any type declared here can be used in any report or FM in the SAP implementation, which is exactly what I do, using the same types for tables in the report and in the FM, as you can see at the declaration of the FMs. The following four types are declared:

ZTABFM01

ZTABFM01

ZTABFM02

ZTABFM02

ZTABFM03

ZTABFM03

ZVBAK_INPUT

ZVBAK_INPUT

The next important thing is Forms. Forms are nothing more than function which you can call inside your report. These are called with the keyword PERFORM and declared between FORM and ENDFORM. They help structure the code and reuse it, as I am doing with vbak_in_to_out.

You could probably identify most of the events used, now that you know how the report works. However, I will explain each one of them:

  • INITIALIZATION (line 36)
    This is how parameters are initialized for the display of the selection screen. The default values for the input, so to say.
  • AT SELECTION-SCREEN (line 67)
    This happens exactly at the moment you click continue at the selection screen. In this example I am using a message code “MESSAGE e888(sabapdocu)” which means, if it is triggered, the selection is not finished, and the user is brought back to the selection screen.
  • START-OF-SELECTION (line 73)
    Happens exactly after AT SELECTION-SCREEN. Is where you actually do the selection and prepare everything for display.
  • TOP-OF-PAGE (line 43)
    Is the display at the top of the page when displaying the first selection. You normally write the page header here.
  • AT LINE-SELECTION (line 77)
    This is the most complex of all events in this example. It is the event triggered by the double click on a line, or the click of the magnifying glass.
    sy-lsind is an important variable in this event, which tells you in which selection screen the report is. If it is equal to 1 then you are selecting items of the first selection, if it is equal to 2 then from the second, etc.
    Lines 83 to 90 is the code used to check if there is any item selected. It goes through the lines of display and checks whether it is an item line and whether the box is checked.
    Lines 93 to 110 just checks if there is any items to display. Just remember that if there is nothing to display we have to set sy-lsind back and tell the report we are still at the first display screen.
  • TOP-OF-PAGE DURING LINE-SELECTION (line 50)
    Uses the same logic of AT LINE-SELECTION to display headers for the subscreens.

One last important thing is line 147. The hide statement allow us to save a variable into memory when double clicking a line, so we can use it for display in the next event (line 115 – wa_vbap).

Please feel free to create this example yourself and make small changes to it. It will surely give you a much deeper understanding of events in ABAP, and about interactive reports. Until next time.


Customizing SAP for Dummies: ABAP Tutorial for Students with some Programming Experience – Part 2

July 30, 2008

In the first part of this tutorial I explained the basics of programs and types in ABAP. Today I will introduce you to the a first ABAP program, which in a way is a Hello World program for ABAP, using the possible features of ABAP in a single simple report. As I said last time a report is nothing more than an executable file.

So here it is:

*&--------------------------------------------*
*& Report  ZSER1301
*&
*&--------------------------------------------*
*&
*&
*&--------------------------------------------*
REPORT  zser1301.

* Declarative Section
TYPES: BEGIN OF zsf_type,
  carrid LIKE sflight-carrid,
  carrname LIKE scarr-carrname,
  currcode LIKE scarr-currcode,
  minpay LIKE sflight-paymentsum,
  maxpay LIKE sflight-paymentsum,
  avgpay LIKE sflight-paymentsum,
  totpay LIKE sflight-paymentsum,
END OF zsf_type.

DATA: i_tab_fl TYPE STANDARD TABLE OF zsf_type,
      wa_tab_fl TYPE zsf_type,
      time_beg TYPE i,
      time_end TYPE i,
      time_tot TYPE i.

* Program Statements
GET RUN TIME FIELD time_beg.

SELECT sflight~carrid scarr~carrname
  MIN( paymentsum ) AS minpay
  MAX( paymentsum ) AS maxpay
  AVG( paymentsum ) AS avgpay
  SUM( paymentsum ) AS totpay
  FROM sflight JOIN scarr
  ON sflight~carrid = scarr~carrid
  INTO CORRESPONDING FIELDS OF TABLE tab_fl
  GROUP BY sflight~carrid scarr~carrname
  ORDER BY sflight~carrid.

GET RUN TIME FIELD time_end.

time_tot = ( time_end - time_beg ).

WRITE:/ 'Carriers Data'.
ULINE.
WRITE:/(8) 'CARRIER', (20) 'NAME',(23) 'MIN PAY',
(23) 'MAX PAY', (23) 'AVG PAY', (23) 'SUM PAY'.
ULINE.

LOOP AT i_tab_fl INTO wa_tab_fl.
  WRITE:/(8) wa_tab_fl-carrid,
  (20) wa_tab_fl-carrname,
  (17) wa_tab_fl-minpay, wa_tab_fl-currcode,
  (17) wa_tab_fl-maxpay, wa_tab_fl-currcode,
  (17) wa_tab_fl-avgpay, wa_tab_fl-currcode,
  (17) wa_tab_fl-totpay, wa_tab_fl-currcode.
ENDLOOP.

WRITE: /, / 'Runtime = ', time_tot.

Now let’s go through it and I will try to explain how all this code works.

Lines 11-19:

Here we declare the structure type we will use in the program. sflight and scarr are two tables declared in the ABAP dictionary. All tables declared in this dictionary are global and may be used in any program. In scarr information about different airlines in store, and in sflight that about flights of these airlines.

Lines 21-25:

We declare an internal table, which is of type of this structure. This means we have one or more entries, and in each entry such an structure. After this we declare a work area, of the same type as the table entry. And declare three integer variables, which we use to calculate the runtime of the report.

Lines 28, 41, 43:

We get the user time before the logic, after it, and then calculate the total.

Lines 30-39:

SQL query. I asume you have some idea of SQL, so I am just going to explain all the ABAP related stuff. We select diferent fields and use some aggregate functions on the paymentsum field of sflight. In order to do this we use group by, and use order by just to have a better output. We are selecting from a join of two different tables.
Pay attention to line 37. In this line we tell the database to store the result of the SQL query into the internal table tab_fl, and to only store the fields of the select, which exist in the internal table.

Lines 45-49:

We display the title and fields of the table.

Lines 51-58:

We display the data store in our internal table. To do this we use a loop at the internal table into the work area. This means that in each iteration of the loop we will get another entry of the internal table in the work area. We use the syntax workarea-fieldname to access the field values.

We let the program run, and get the following screen:

Screen of Report ZSER1301

Screen of Report ZSER1301

That was not so difficult, was it? I know you are probably thinking this is not the kind of program you program in every language, but it is the most typical report in ABAP. Actually, a report is call this way, because you get information and report it back.

So now that we have written a program that works, let’s try to write the same thing and use the right tools for it.

In the next example we will do exactly the same. For it we will declare the structure type in the ABAP dictionary and create a function module (FM), which works like a subroutine, to get the information from the database. This way we make use of the principle of modularity, reusability and continuity.

So the first thing we do, is create the structure type, using the object explorer:

ZSF_TYPE Structure

ZSF_TYPE Structure

We rewrite the report:

*&--------------------------------------------*
*& Report  ZSER1300
*&
*&--------------------------------------------*
*&
*&
*&--------------------------------------------*
REPORT  zser1300.

* Declarative Section

*TABLES: scarr, sflight.

DATA: i_tab_fl TYPE STANDARD TABLE OF zsf_type,
      wa_tab_fl TYPE zsf_type,
      time_tot TYPE i.

* Program Statements

CALL FUNCTION 'ZSER1300_FM'
  IMPORTING
    time_tot = time_tot
  TABLES
    tab_fl   = i_tab_fl.

WRITE:/ 'Carriers Data'.
ULINE.
WRITE:/(8) 'CARRIER', (20) 'NAME',(23) 'MIN PAY',
(23) 'MAX PAY', (23) 'AVG PAY', (23) 'SUM PAY'.
ULINE.

LOOP AT i_tab_fl INTO wa_tab_fl.
  WRITE:/(8) wa_tab_fl-carrid,
  (20) wa_tab_fl-carrname,
  (17) wa_tab_fl-minpay, wa_tab_fl-currcode,
  (17) wa_tab_fl-maxpay, wa_tab_fl-currcode,
  (17) wa_tab_fl-avgpay, wa_tab_fl-currcode,
  (17) wa_tab_fl-totpay, wa_tab_fl-currcode.
ENDLOOP.

WRITE: /, / 'Runtime = ', time_tot.

and in line 20 we call the function module ZSER1300_FM and pass the variables which are going to be return.

If you take a look at the interface of ZSER1300_FM, you will see the declaration of the variables used in the FM. All not optional will have to be assigned at the time of the FM call.

FUNCTION ZSER1300_FM.
*&--------------------------------------------*
*"Local Interface:
*"  EXPORTING
*"     REFERENCE(TIME_TOT) TYPE  I
*"  TABLES
*"      TAB_FL STRUCTURE  ZSF_TYPE
*"  CHANGING
*"     REFERENCE(TIME_BEG) TYPE  I OPTIONAL
*"     REFERENCE(TIME_END) TYPE  I OPTIONAL
*&--------------------------------------------*

GET RUN TIME FIELD time_beg.
SELECT sflight~carrid scarr~carrname
  MIN( paymentsum ) AS minpay
  MAX( paymentsum ) AS maxpay
  AVG( paymentsum ) AS avgpay
  SUM( paymentsum ) AS totpay
  FROM sflight JOIN scarr
  ON sflight~carrid = scarr~carrid
  INTO CORRESPONDING FIELDS OF TABLE tab_fl
  GROUP BY sflight~carrid scarr~carrname
  ORDER BY sflight~carrid.
GET RUN TIME FIELD time_end.

time_tot = ( time_end - time_beg ).

ENDFUNCTION.

I hope after reading through all the code you will feel more comfortable with ABAP. To all of you who follow the tutorial, I want to thank you and encourage you to write comments about what you would like to see more of, or what you do not like about the tutorial. Thanks! See you next time.

Just as a small disclaimer: All the code written in this blog has been writen by me, during my free time and is not used in any way at my work. All the information has been either obtained from the SAP help site or from talks with other people.


Customizing SAP for Dummies: ABAP Tutorial for Students with some Programming Experience – Part 1

July 19, 2008

As you know, I am going through some kind of ABAP training right now with the help of one of the team members. I have spend the last two weeks reading the documentation of help.sap.com and feel I am starting to get a better grip of what ABAP is and how you can use it.

The goal of this tutorial is to give any person with the basic knowledge of procedural programming, object-oriented programming (OOP) and SQL, a good overview of what the possibilities of the language are and how to understand a basic ABAP program.

So let’s start.

Programs in SAP are created, edited and debugged in the same presentation framework you normally use to access the SAP programs. These programs are called Transactions and are either local programs or belong to a package. The only reason for using packages is to group different files that belong together and make the transportation phase easier. Transportation meaning changing the status of a program, from the programing phase to the testing phase, to finally transporting the program to the SAP master.

Any program written in ABAP begins with one of the following introductory statements, a space and the name of the program:

  • REPORT
  • PROGRAM
  • FUNCTION-POOL
  • CLASS-POOL
  • INTERFACE-POOL
  • TYPE-POOL

What you normaly use is a report. A report is nothing more than an executable programm. Function pools are, as its name says, pools of functions you can include in your executable program. The same for type pools, which contains definitions of types to use in executable programs. Class pools are used in OOP, so we will come to that later (when I get to the topic in my training :-) )

Once you have defined the type of program and its name, you can start with the types and data definitions. I will explain both together as their similarities are many. To define a new type you have following syntax

TYPES new_type
{TYPE {REF TO} defined_type} |
{LIKE {REF TO} defined_variable}.

This code defines a new type new_type based on the already defined type defined_type or based on the type of the variable defined_variable. If you use the keyword REF TO the new type will be only a pointer to a variable of that type.

Analogous we can use following syntax to define a variable:

DATA new_type{(c)}
{TYPE {REF TO} defined_type} |
{LIKE {REF TO} defined_variable}
{VALUE d}.

Just notice that if you use the option (c) you will create an array of c elements of the defined type, and using the keyword VALUE you can assign a value d to the variable.

And for the end of this part I just want to show you the predefined types in ABAP you can use when defining new types or variables.

Numeric Types

Type Identifier Initial Value Lenght (in Bytes) Format
Integer I 0 4 -
Floating point number F 0 8 -
Packed number P 0 1-16 0000- (negative) / 0000 (positive)

Character Types

Type Identifier Initial Value Lenght (in Bytes) Format
Characters C ‘ ‘ 1 – 65535 -
Date D ‘00000000 ‘ 8 YYYYMMDD
Time T ‘000000 ‘ 6 HHMMSS
Hexadecimal field X 0 1 – 65535 X’0000..000′

Hope you have now a better understanding of what the basics of ABAP are. I will come back with more information next time. Stay tuned!


TPS Report

July 9, 2008

It has already been a bit more than 2 weeks of Internship so far, and I actually wanted to write something about my work. I even had something almost written. Well, here is the thing: I am not allowed to do it. Everything is confidential, everything is top secret.

The only facts I MAY be able to tell you, because I have already told everyone about it anyway, are:

  • I work with SAP technology (is pronounced S.A.P. for those of you who have never heard about it before)
  • I will probably learn some ABAP, the programming language which SAP is programmed in (this is called abap and not A.B.A.P.)

I guess now that you know that, I can tell you what SAP is all about.

SAP is a system used in companies to plan and organize the business process, and specially at the same time manage the financial aspect of the company. There are different areas of the system which can be implemented for a company, and inside this areas many different modules. These modules are already implemented in the original SAP version, but of course no two companies are the same. So that is when you have to call the consultants. They come and see how your business process works, write the requirements for the implementation and send the job to India to be done, what is called outsourcing.

Here in India, the modules get implemented, and sent to the client. To implement these modules you use ABAP (Allgemeiner Berichts-Aufbereitungs-Prozessor) which is an object oriented programming language, with SQL queries in it, which is nothing more than the use of tables and information from these.

Well, this much I can tell you right now. I will come back later with some more information, just understand that things do not go as fast here as at university, and we specially do not get to normalize databases. Well at least I don’t :-)