Menu

Getting Access to Out-of-Scope Variables with Field Symbols

DevWorkbench    Monday March 4th, 2013   

Recently, I came across a situation which required me to modify an internal table which was not in the scope of my program. My client wanted to take over the “System Condition” indicator from a maintenance task list into the maintenance item. A task list can be referenced in each item of a maintenance plan, but the System Condition field is not carried over automatically.

However, I had no means of accessing this field directly. The only way to get control over the program flow in the transactions IP01/IP02/IP10 was the user exit IPRM0004 (customer check of maintenance plan at save), but there was no way to return any data changes to the calling function group IWP3 from this point. I would have needed to access the table IMPOS, which contains all maintenance items for the maintenance plan.

The following diagram shows the situation.

Situation in the Customer Exit IPRM0004

Situation in the Customer Exit IPRM0004

The question now was to modify the table IMPOS when the interface of the function module EXIT_SAPLIWP3_004 didn’t give me any possibility to return data to the calling program.

So, what did I do? ABAP offers a way to get access to any variable which is currently defined in the global scope, and this way is via field-symbols and the dynamic assign technique. Let’s start with a simple example, which demonstrates a static assign.

* 1. Define a local variable and a field symbol
 DATA lv_var TYPE CHAR4 VALUE ‘ABCD’.
 FIELD-SYMBOLS <fs_field> TYPE CHAR4.

 * 2. Assign the field-symbol to the local variable and modify it.
 ASSIGN lv_var TO <fs_field>.
 <fs_field> = ‘DCBA’.

 * 3. Output the data.
 WRITE lv_var.

Quite easy so far – the program would output ‘DCBA’, because the field symbol was assigned to point to the variable lv_var and its value was changed via the field symbol. The next example shows the dynamic assign. It’s called dynamic because the field symbol can be pointed to a variable whose name is only known at run time. To do this, we need to put the name of the variable in a string literal in brackets, like this:

* 1. Define a local variable and a field symbol.
DATA lv_var TYPE CHAR4 VALUE ‘ABCD’.
FIELD-SYMBOLS <fs_field> TYPE CHAR4.

* 2. Assign the field-symbol to the local variable and modify it.
ASSIGN (‘lv_var’) TO <fs_field>.
<fs_field> = ‘DCBA’.

* 3. Output the data.
WRITE lv_var.

The brackets tell the system to assign the field symbol to the variable name returned by the expression between the brackets. In this case it’s a character literal, it could also be a variable which has the value ‘lv_var’. Now, in the third step we will access a variable which is not in the scope of the current program, but loaded in the global runtime environment. To do this, we need to know the name of the program where the variable is loaded, and – again – put it in brackets. I will now use the exact coding I did for the client as an example.

* 1. Define field symbols for the internal table and its row type.
FIELD-SYMBOLS <fs_impos> TYPE TABLE.
FIELD-SYMBOLS <fs_mpos> TYPE wc_wmpos.
DATA lv_anlzu TYPE anlzu.

* 2. Assign the field symbol to the table in calling program
ASSIGN ('(SAPLIWP3)IMPOS[]') TO <fs_impos>. 

* 3. Loop over each maintenance item, check if it has a task list.
*    If a task list is assigned, update the maintenance plan’s
*    system condition from it.
LOOP AT <fs_impos> ASSIGNING <fs_mpos>.
* Task list assigned?
  IF NOT <fs_mpos>-plnnr IS INITIAL.
*   Read system condition from task list header
    SELECT SINGLE anlzu
      FROM plko
      INTO lv_anlzu
      WHERE plnty = <fs_mpos>-plnty
        AND plnnr = <fs_mpos>-plnnr
        AND plnal = <fs_mpos>-plnal.

    IF sy-subrc = 0 AND lv_anlzu <> <fs_mpos>-anlzu.
*     The system conditions in plan and task list differ, update
      <fs_mpos>-anlzu = lv_anlzu.
    ENDIF.
  ENDIF.
ENDLOOP.

So, what are we doing here? The first step contains one peculiarity:

FIELD-SYMBOLS <fs_impos> TYPE TABLE.

We don’t type the field symbol to the exact type of the internal table IMPOS, because field symbols don’t allow us to do that. Instead, we use the generic type TABLE. Next, we do the assignment, which looks like somebody hit all the number keys on their keyboard with caps lock on:

ASSIGN ('(SAPLIWP3)IMPOS[]') TO <fs_impos>.

Let’s look at that expression step by step. We’ve seen the character literal in brackets before; in our second example. So we assign our table-typed field symbol to this variable:

(SAPLIWP3)IMPOS[]

ABAP Developers will recognize the square brackets to indicate an internal table with header line – if they were omitted, our field symbol would point to the table’s header line and we would get a dump because of the type mismatch. What remains is the program name of the program where the variable is loaded. If we put that in brackets in front of the actual variable name, the field symbol will point to the variable in this program. By the way – with this technique you can also access variables outside of the current scope in the debugger.

The remaining part of the program should be rather easy to understand. We loop over the maintenance positions, using the ASSIGNING technique we get the field symbol <fs_mpos> to point to each single row. They are then checked for existence of a task list and, if one is found, the respective system condition is read and saved in the maintenance position.

Now, because all this was done before the COMMIT WORK, the system will handle this as if the user did the update himself and will save the changed system condition flag to the database.

Just another little hint here: Assigning pointers all over the place is not considered very good development practice. In this particular case, I didn’t have any other choices, but you should make sure there is no other way to achieve what you want to do before you do this little piece of hacking.