sergey.korolev

6 Posts

As you might see in different ABAP forums people ask repeatedly for a function module doing some simple date calculation, e.g. end of month or weekday calculation. The first question, the rhetorical one - "why on earth they cannot search the SAP repository themselves" - definitely has no answer. We must assume that there is a kind of people whose mentality prevents them from trying to solve problems themselves; they always seek help from others in their everyday duties.

The other question is why at all one needs an FM for these particular calculations. Just think: ABAP has a special data type for dates, and it has built-in efficient date arithmetic, so why not to use it.  

For example, if you need to add a number of days to a date, you can use a simple ADD statement or arithmetic ‘+' operation, where one operand is of type d and the other is of type integer.

Another example is weekday calculation. Knowing that 01/01/1900 was Monday, you can always calculate a weekday by the simple statement:

CONSTANTS: c_known_monday TYPE d VALUE '19000101'. "It's Monday 
weekday = ( given_Date - c_known_monday ) mod 7 + 1.

The result will be the number of range from 1 to 7, where 1 is Monday and 7 is Sunday. This calculation will work also correctly for dates before 01/01/1900. You can use the result as a key for T246 SAP table, which stores weekday names in different languages.

A little harder is end-of-month calculation. By the way, you can find in SAP system repository several FMs calculating an end-of-month day, with more or less correct leap year calculation. However, the thing is that having built-in ABAP date arithmetic you hardly need an FM, and you don't need to calculate a leap year.

Suppose, you have a variable SOMEDATE with arbitrary date. First, we calculate the first day of a month (remember, internally date is represented in ABAP as YYYYMMDD):

SOMEDATE+6(2) = ‘01'.

Next, we have to find some date in the next month. Knowing the first day of the month, we can add, for example, 31 days to that day, and the result will obviously be in the next month boundaries:

ADD 31 TO SOMEDATE.

Next, let's again calculate the first day of the month (in this step this will be the next month):

SOMEDATE+6(2) = ‘01'.

Now as we have the first day of the next month we can just subtract 1 day, and the result will be our end-of-month day:

SUBTRACT 1 FROM SOMEDATE.

So, the calculation consists of just four statements:

SOMEDATE+6(2) = ‘01'.
ADD 31 TO SOMEDATE.
SOMEDATE+6(2) = ‘01'.
SUBTRACT 1 FROM SOMEDATE.

No IF's, no CASE's. The good idea is to encapsulate this code snippet into a parametric macro definition; no need for another function module as the cost of its call will be far beyond the payload.

And what about elegance? Frankly, I don't know, it was just to catch your eye.

On my recent project among others, I was assigned a task of connecting a Workflow to the event of Sales Document status change.  As you may know any Sales Document can have different statuses, and those statuses can be freely customized via so called Status Profiles. Actually, Status Profiles and Statuses can be used with other business documents and entities throughout the SAP system.

Generally, Status Profile consists of several individual statuses, which can be changed in specific order, each status can be linked to a particular authorization group, and also it can influence corresponding business transaction, e.g. restrict change access to the document if it is in specific Status. All in all, Status Profile is a universal tool, and in particular it can be used for designing quite sophisticated approval process, making a strong temptation to employ Business Workflow functionality.

And when workflow technology involved, you always have a task of connecting specific workflow template to some Business Object event. Some application events of standard business objects are fired by the system, and some are not. Often, you can link a particular event to a specific Change object – this is done in a transaction SWEC. Though, linking events to change documents is not always reliable as they can be switched off due to database volume affect or other Basis team consideration.

In case of Statuses the problem is that they are stored in a separated table JEST, so you cannot link Status change via Business document change object. However SAP has special customizing tables containing linkage between Status change and Business Object event. This is done in the transaction BSVW. Let us call the moment when one status becomes inactive and another becomes active a Status change.Actually there is more than one customizing table sets in the system which affects the Status change and BO Event linkage, and the system takes into account all of them.

  • Tables BSVWCOUP1  and BSVWCOUP2 store system settings which are not supposed to be altered by the Customer; this table set is always taken into account
  • Tables BSVWCOU3  and BSVWCOUP4 store old customer settings, and are taken into account unless the Customer configured the new settings tables
  • Tables BSVWCOU5 and BSVWCOUP6 store new customer settings.

Old and new customizing tables differs in primary key declaration, and as a consequence, using new customizing you can assign different Status changes to the same BO event.

The precise usage of all these tables you can see in the source code of the function module SWE_EVENTS_FOR_STATUS_GET which is called whenever status of any object in the system changes. Also the source code helps us to understand the configuration principles of Status Change – BO event linkage; to get the key of it, imagine the process: when some status is assigned to a business document (or any other entity) for the first time, the system is aware of only one status – the new one; in case of status change, there are always two statuses involved – the new one (which becomes active) and the old one (which becomes inactive).

When calling SWE_EVENTS_FOR_STATUS_GET the system passes to the FM an internal table with a status change signature – both old and new statuses, and to find a link to a BO event the FM analyses configuration tables searching for the exact match – a pair of entries, one of them denoting the old status (with inactive state) and the other – the new status. In other words, the system takes into account not only a particular status which was assigned to a document, but also the order in which statuses were changed.

In the following example we see the linkage between the Status profile (of Schema) and Business Object Event.  Starting from SAP 4.7 you can use ABAP classes instead of old flavor business objects in Workflow. However, it is obvious that this customizing does not accept ABAP classes. The very first key field of this table is Status Object Type (or Object Category), it defines a kind of business entity to which you can assign a Status profile. The VBK object category represents Sales Order Header.

image

For such an entry, you also have to define Status restrictions (see the folder icon in the tree to the left of table view).  Status restrictions define a particular status change which will invoke the specified BO event. See the screen shot below:

image

This pair of entries represents a moment when Status of object changes from ACCP to RJCT. Note that “Inact.” checkbox column corresponds to inactivity state.More than one Status change (belonging to the same or different Status Profile) can be assigned to a single particular Business Object event. To help you differentiate the Workflow behavior depending on the Status change order the system supplies additional parameters to the event container:

  • STATUS_ON – for statuses which become active
  • STATUS_OFF – for statuses become inactive

Both parameters are multiline and contains only 5-character internal status representation, such as E0002 (user status) or I0002 (system status), though I could not model a situation when the function passes with the event more than one status in the container parameter. Do not forget to specify these parameters, when defining Business object events for Status change.

Interesting enough that according to the source code of the function module SWE_EVENTS_FOR_STATUS_GET you can use function modules for evaluating Business Object Type and its event names to be fired, however the fields in which you can set those function module names are not displayed in the maintenance dialog. Maybe this feature is planned for future releases. 

Sergey Korolev

DO and TRY

Posted by Sergey Korolev May 14, 2005

In my previous DO and DO while praising DO..ENDDO construct I have placed a warning on possible OO exception overhead when using TRY...ENDTRY instead. My preconceived idea looked very obvious to me - I thought that OO exception mechanism should have quite a reasonable impact on perfomance.

And after reading Horst Keller comment on that weblog (thanks Horst!) I decided to clarify this at least to myself. And now I present the results to you.

 

First, I have written a simple test program to check different techniques of raising exceptions. Here you can see five subroutines. In each subroutine the statement of my interest is placed within quite long loop, just to minimize sporadic events influence and to make timings more stable.

First subroutine contains four nested IFs - the construct I consider to be quite unreadable and ugly. Anyway, as I expect, it should give the shortest execution time. The essential part of the each subroutine is a sequence of internal table READs, which is quite common code pattern for nested IF. The sequence is deliberately created to make sure that only the last READ would be successfull.

FORM test_if_endif USING value(pernr). DO 100000 TIMES. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc NE 0. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc NE 0. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc NE 0. READ TABLE tab INTO wa WITH KEY pernr = pernr BINARY SEARCH. IF sy-subrc NE 0. CONTINUE. ENDIF. ENDIF. ENDIF. ENDIF. ENDDO. ENDFORM. "test_if_endif

Next subroutine contains DO 1 TIMES...ENDDO loop as a replacement for nested IF.

FORM test_do_enddo USING value(pernr). DO 100000 TIMES. DO 1 TIMES. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. CHECK sy-subrc NE 0. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. CHECK sy-subrc NE 0. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. CHECK sy-subrc NE 0. READ TABLE tab INTO wa WITH KEY pernr = pernr BINARY SEARCH. CHECK sy-subrc NE 0. ENDDO. ENDDO. ENDFORM. "test_do_enddo

The following subroutine is a sample of using TRY..ENDTRY with raising exception of a particular type without creating its instance (RAISE EXCEPTION TYPE).

FORM test_try_endtry USING value(pernr). DO 100000 TIMES. TRY. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION TYPE cx_alert. ENDIF. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION TYPE cx_alert. ENDIF. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION TYPE cx_alert. ENDIF. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION TYPE cx_alert. ENDIF. CATCH cx_root. ENDTRY. ENDDO. ENDFORM. "test_try_endtry

The following subroutine is a sample of using TRY..ENDTRY with raising exception with previously created instance (RAISE EXCEPTION ex). The instance was created before subroutine call.

FORM test_try_endtry_with_instance USING value(pernr). DO 100000 TIMES. TRY. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION ex. ENDIF. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION ex. ENDIF. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION ex. ENDIF. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION ex. ENDIF. CATCH cx_root. ENDTRY. ENDDO. ENDFORM. "test_try_endtry_with_instance

The last subroutine is a sample of raising exception with creating an instance in CATCH clause (CATCH ... INTO).

FORM test_try_endtry_with_instance1 USING value(pernr). DATA: ex TYPE REF TO cx_root. DO 100000 TIMES. TRY. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION TYPE cx_alert. ENDIF. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION TYPE cx_alert. ENDIF. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION TYPE cx_alert. ENDIF. READ TABLE tab INTO wa WITH KEY pernr = space BINARY SEARCH. IF sy-subrc = 0. RAISE EXCEPTION TYPE cx_alert. ENDIF. CATCH cx_root INTO ex. ENDTRY. ENDDO. ENDFORM. "test_try_endtry_with_instance1

I tested the program containing all subroutines by Runtime Analysis tool (SE30). Testing environment: SAP_BASIS - 6.20, WAS Operating system - HP-UX, RDBMS - ORACLE 9.2.0.5.0. I was the only user online and there were no background tasks.

Now, the results from SE30. I remind you that all timings are in microseconds.

First picture shows execution times of subroutines when READ TABLE statement ends successfully (sy-subrc = 0) and in that case exception CX_ALERT is raised in three subroutines.

image

Next picture show the results when none of READ statement was successfull and so the exception was not raised.

image

An interesting result (at least for me) is that preliminary creation of an OO exception instance (subroutime try_endtry_with_instance) has negative impact on performance, and in that case an overhead is around 0,7 microsecond per raising an exception comparing to raising exception with type (RAISE EXCEPTION TYPE ...).

Another theoretical result is that DO 1 TIMES...ENDDO is slightly slower than TRY...ENDTRY without raising and exception.

What could be the practical conclusions?

  • First, raising exception with statement RAISE EXCEPTION TYPE has no essential overhead. In my test it cost only 1 microsecond. Very subtle.
  • Second, catching exception with instance creation (CATCH ... INTO ...) cost around 20 microseconds. So, when processing one million record internal table you can potentially gain 20 sec by avoiding such an exception catch method within loop. Is it worth something? Not very much, I guess.

All in all, I really impressed by the results and plan from now on to use TRY...ENDTRY instead of nested IF where possible.

Sergey Korolev

DO and DO

Posted by Sergey Korolev Apr 15, 2005

Personally I hate nested IF ... ENDIF. Suppose the following:

READ TABLE some_table WITH KEY ... IF sy-subrc = 0.    READ TABLE some_other_table WITH KEY ... </li>  IF sy-subrc = 0.</li>    READ TABLE another_table WITH KEY ... </li>    IF sy-subrc = 0.      ... etc, etc..    ENDIFENDIF.ENDIF.

"Most disturbing, sir" as Jeevse could put it. Very unpleasant.

And the idea's come. Now instead of nested IF's I use DO..ENDDO, just as follows:

DO 1 TIMESREAD TABLE some_table WITH KEY ...   CHECK sy-subrc = 0.    READ TABLE some_other_table WITH KEY ...   CHECK sy-subrc = 0.  READ TABLE another_table WITH KEY ...   CHECK sy-subrc = 0.  ... etc, etc..ENDDO.

That's it. What do you think?

 

Preamble

 

 

 

BADi is

an object oriented version of SAP enhancement concept. Its main idea is that

instead of filling special includes with ABAP code in some customer function style='mso-bidi-font-weight:normal'>

EXIT_SAPxxxxxx

style='_nnn

 

style=',

 

you are to define some ABAP object class which implements a particular

interface (BADi interface) – a set of predefined method declarations.

 

 

 

What are

the pros of the BADi concept besides the fact it is object-oriented (though I

know some people think OO overweighs all others pros and cons)? At my recent

project I was assigned a role of a development team leader and so I was aware

of software engineering management. And from this point of view BADI is much

better than customer exits. Sometimes more than one developer has different

tasks which include programming of the same customer-exit. One of the possible

consequences is a mixture of the code provoking errors, transport request

locking problems, etc. On the contrary, a particular BADI can have several

independent implementations, and so different tasks can be safely separated

with no source code conflicts or request deadlocks.

 

 

 

And one

more idea... There are still lots of questions on various SAP ABAP forums (including

SDN) concerning ways of searching possible enhancements of one or another

business transaction. BADi

  gives you one stable method of searching:

just place a break-point inside the method

CL_EXITHANDLER=>GET_INSTANCE

 

style=' and then execute the business transaction. After

the program stops at the break-point you can easily obtain BADi name and browse

the source code around the call point to get the most exact knowledge of

  “terms and conditions” of the BADi call.

 

The Problem

 

 

In our

project we had to implement HR functionality, and there were several development

tasks including default value calculations for various infotypes (infotype is a

time dependent chunk of a person attributes – e.g. personal data, address data,

etc., infotype has its unique four-digit code).

 

 

 

In transaction

SMOD you can find a customer enhancement designed for this kind of task - style='mso-bidi-font-weight:normal'>PBAS0001. The enhancement contains

function module EXIT_SAPFP50M_001

,

which is commonly used for setting infotype defaults, and

  EXIT_SAPFP50M_002

 

for additional checks after a user has entered values into infotype fields.

 Both user-exits have their counterparts in

BADi HRPAD00INFTY

. These are corresponding

methods IF_EX_HRPAD00INFTY~BEFORE_OUTPUT

 

and IF_EX_HRPAD00INFTY~AFTER_INPUT

 

and they are called from nearly the same call-up points of the source code.

 

 

 

It would be

nice to use them, but the matter is that those methods have only importing

parameters and obviously they were not intended to change infotype values.

 

 

 

And after hard

reflections and mental sweating on possible ways of the tasks separation I found

a solution – to create my own BADI and call it from the EXIT_SAPFP50M_001

and EXIT_SAPFP50M_002

.

 

 

Creating BADI

 

 

So, let’s

start with transaction SE18. At the initial screen I entered the name – style='mso-bidi-font-weight:normal'>ZPBAS0001_BADI_FLT and clicked the

button Create

.

 

 

 

By checking

Multiple use

checkbox I allowed my

BADI to have multiple implementations, and that is the main goal of the

proceeding. Now each developer (informed of the BADI existence) can create

his/her own fully independent implementation.

 

Filtering

 

 

But here we
can improve the BADI. As we make developments for HR functionality the 
infotype code will always be involved. Different infotypes
have different structure and we cannot make universal code for calculating
default values. What I am leading to is that every implementation of the newly
created BADI will be essentially infotype dependent. In that case I bet the
very first statement of every implementation would be 

>
CHECK
> infty = some_infotype_number.

Thus infotype code is an ideal candidate to become a filter value for implementations. In the Attributes tab of the BADI definition you can see a checkbox Filter-depend. The checkbox turns on filter capabilities of the BADI.

image

To finalize definition of filter dependent BADI we have to enter some data element name into Filter type field, and the data element has to accept infotype codes as a value.

There are special requirements for the data element to be used as a Filter type. It has to be associated with some Search help, and one of the import parameter of the search help must be of the same type as data element. So, I was not successful when tried hastily to enter INFTY data element into the Filter type field as it has no associated Search help. I did not find proper SAP standard data element (maybe I did not search thoroughly) and had to create one of name ZINFTY_FLT.

image

Also I have created a Search help ZH_T582A_BADI with export parameter of type ZINFTY_FLT.

image

Finally, I associated previously created Search help with new Data element.

image

After that I could successfully enter the name of my Data element as the Filter type of the BADI.

To be honest, the requirement looks odd. At least, I do not clearly understand the reason for an idea of obligatory Search help connection. Certainly defining filter values with Search help is convenient but whats wrong if one can enter values manually?

Why use Filtering? One can think that all the mess is about getting rid of one CHECK statement. But do not forget that without filtering every time the BADI fires it creates class instances for all implementations defined and that costs.

]]>

 

Next step

 

 

Now it’s

time to define methods of the BADI interface. It is done in the style='mso-bidi-font-weight:normal'>Interface tab of BADI definition. Here

I can enter method names. Being not much eccentric I called them style='mso-bidi-font-weight:normal'>GET_DEFAULT_VALUES and style='mso-bidi-font-weight:normal'>CHECK_BEFORE_UPDATE.

 

 

 

By double

clicking at the method name we are driven into a common class/interface

designer (SE24). And here we define all the method parameters. Note that each method

has one predefined import parameter

 FLT_VAL of type ZINFTY_FLT for we defined the

BADI as filter dependent.

 

 

 

image

 

 

 

 

 

Saving

 

 

Now the

definition of the BADI seems to be completed and we can click the button style='mso-bidi-font-weight:normal'>Save. As a result the BADI interface

will be activated and a special proxy class will be generated and also

activated.

 

At the call-up point

 

 

The BADI

now is almost ready for an implementation. The final stroke is to call it up

somewhere. At the beginning of EXIT_SAPFP50M_001

 

function module we insert a special aided fragment of code. First, by

calling   method style='mso-bidi-font-weight:normal'>CL_EXITHANDLER=>GET_INSTANCE

style='

we obtain an instance of the proxy

class, implementing BADI interface, and examine whether or not any

implementation exists.

 

 

If it does,

then we call appropriate method of the interface instance which does the rest.

Note that before the call we   calculate

proper filter value, and in this case it is an infotype code. At the first

glance it seems slightly odd that we place a single call to the BADI method while

it was defined as Multiple use

. No

internal tables, no loop. Actually the interface instance is the instance of

the proxy class which was generated after saving BADI definition. Each proxy

class method contains a standard code snippet which finds and calls all the

implementations complying with filter value.

 

 

 

Global data

declarations:

 

 

 

 

 

 

>CLASS

> cl_exithandler style='mso-bidi-font-weight:normal'>DEFINITION LOAD

.

 

 

 

DATA

:

 

  act_imp_existing TYPE

sxrt_boolean,

 

  pbas0001_badi_instance style='mso-bidi-font-weight:normal'>TYPE REF

TO

 

zif_ex_pbas0001_badi.

 

CONSTANTS:

 

  c_exit_name TYPE

exit_def VALUE

 

'ZPBAS0001_BADI'.

 

 

 

 

 

 

 

BADi call:

 

 

 

 

 

 

 

 

*&----


*

 

*&   Include

           ZXPADU01

                     

                   *

 

*&----


*

 

*"----

-


 

 

""Lokale Schnittstelle:

 

*"   IMPORTING

 

*"      VALUE(TCLAS) LIKE

  PSPAR-TCLAS

 

*"      VALUE(INNNN) LIKE

  PRELP STRUCTURE

  PRELP

 

*"      VALUE(IPSYST) LIKE

  PSYST STRUCTURE

  PSYST

 

*"      VALUE(I001P) LIKE

  T001P STRUCTURE

  T001P

 

*"      VALUE(I503) LIKE

  T503 STRUCTURE

  T503

 

*"   EXPORTING

 

*"      VALUE(INNNN) LIKE

  PRELP STRUCTURE

  PRELP

 

*"   CHANGING

 

*"      REFERENCE(IPREF) LIKE

  PREF STRUCTURE

  PREF

 

*"----

-


 

 

 

 

IF

pbas0001_badi_instance style='mso-bidi-font-weight:normal'>IS INITIAL

.

 

  CALL

 

METHOD

cl_exithandler=>

get_instance

 

    EXPORTING

 

 

      exit_name

                     = c_exit_name

 

    IMPORTING

 

 

      act_imp_existing

            

 = act_imp_existing

 

    CHANGING

 

 

      instance

                      = pbas0001_badi_instance

 

    EXCEPTIONS

 

 

      no_reference

                  = 1

 

      no_interface_reference

        = 2

 

      no_exit_interface

             = 3

 

      class_not_implement_interface

= 4

 

      single_exit_multiply_active

   = 5

 

      cast_error

                    = 6

 

      exit_not_existing

             = 7

 

      data_incons_in_exit_managem

   = 8

 

      OTHERS

                        = 9.

 

 

 

  IF

 

sy-subrc <> 0.

 

    MESSAGE

 

ID

sy-msgid

TYPE

sy-msgty

NUMBER

sy-msgno

 

               WITH

sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.

 

  ENDIF

.

 

ENDIF

.

 

 

 

IF

NOT

act_imp_existing IS

INITIAL

.

 

  CALL

 

METHOD

pbas0001_badi_instance->

get_default_values

 

    EXPORTING

 

 

      tclas

   = tclas

 

      ipsyst

  = ipsyst

 

      i001p

   = i001p

 

      i503

    = i503

 

      flt_val =

innnn-infty

 

    CHANGING

 

 

      innnn

  = innnn

 

      ipref

  = ipref.

 

ENDIF

.

 

 

 

 

 

 

 

Implementation

 

 

To create a

BADI implementation we start SE19 transaction. After entering implementation

name and BADI definition name you also can manually enter the name of the class

already existent which will implement the BADI, or the system can generate it

automatically.

 

 

 

Strange

enough, for some reason or another a single class cannot be used for

implementing more than one BADI, in spite the fact that a class can implement

arbitrary number of interfaces. There could be circumstances when it is convenient

to use a single class to implement more than one BADI. Let’s treat it as an

item to a wish list for coming versions of ABAP.

 

 

 

I intend

this particular implementation to create some custom infotype automatically

when user executes some personnel management action (infotype 0000) or changes

somehow organizational assignment (infotype 0001). At the Defined filters

ALV control I insert two items: 0000 and 0001.

 

 

 

image

 

 

 

 

 

 

Finally,

after entering appropriate code into a method implementation we can activate

implementation and begin testing and debugging.

 

 

!https://weblogs.sdn.sap.com/weblogs/images/3886/Image7_resize.gif|height=400|alt=image|width=533|src=https://weblogs.sdn.sap.com/weblogs/images/3886/Image7_resize.gif|border=0!</body>

Preventing sporadic lock conflicts when calling BAPI&#146;s

Suppose you need to generate a chain of supply documents automatically via BAPI, or you have to call the same BAPI several times &#150; e.g. from within a LOOP-ENDLOOP statement. In such circumstances you might face a problem when system reports that you are trying to lock an object which already locked by yourself . This is a Deadlock. And at the same time you definitely know that you did not place any lock. Also the situation may occur when you select application table immediately after successful BAPI call and committing work and the SELECT statement returns sy-subrc = 4, that is nothing found. The things are getting worse by sporadic nature of the error.

What&#146;s behind it?

A number of BAPIs, particularly those posting various documents (financial documents, purchase/sales orders, material movements etc.); indirectly call function modules updating database tables asynchronously. So do all the common dialog transactions. This is a main part of SAP update process. As a rule before calling update functions BAPI locks some functionally related objects to prevent data inconsistencies.

The main goal of the asynchronous update mechanism is to optimize system response time for users working in dialog mode. Roughly speaking, the dialog transaction does all the sophisticated checks on-line (and reports errors to a user if any) and prepares a consistent data bundle (e.g. document header and items) and then leaves all the routine work of physical updating database tables to another background task. And that background task may run even on a different application server.

Technically this looks as follows: when you call some function module with addition IN UPDATE TASK actually the function does not start execution, instead its name and actual parameter values with a unique key are written to the special set of tables which also can be called an update queue (those are tables VBHDR, VBMOD, VBDATA). And at the moment you issue a COMMIT WORK statement the special aided parallel task (a.k.a. update work process) starts processing the update queue. And the control immediately returns to the calling program. And at that moment it&#146;s not guaranteed that all the database tables already updated with actual data. So, you may get sy-subrc = 4 after selecting data from application tables with a right key value returned from the BAPI.

The treatment for the &#145;sy-subrc = 4&#146; error is COMMIT WORK AND WAIT statement. The usage of COMMIT WORK AND WAIT can be called synchronous update technique as the control returns to the calling program only after all physical updates committed to the database. But COMMIT WORK AND WAIT cannot help with lock conflict.

Why?

That&#146;s because of update processing sequence. First, update work process does really call all the update function modules, which issue SQL statements for inserting/updating records in application tables, then (if there is no errors) update work process issues database commit &#150; remember that at this point the program which waits at COMMIT WORK AND WAIT can continue working. And finally only after committing database updates work process starts to release SAP locks. So, as both calling program and update work process works in parallel it is not guaranteed that immediately after returning from COMMIT WORK AND WAIT all the locks previously locked in BAPI are released.

Fortunately there is a clue. You can successfully prevent both lock conflict and &#145;sy-subrc = 4&#146; error by employing local update mode. It can be turned on by issuing SET UPDATE TASK LOCAL statement before calling BAPI. That means that all the updates which were supposed to be in parallel update task would be processed (together with lock release) in the same calling task as part of COMMIT WORK statement processing. Note that both COMMIT WORK or ROLLBACK WORK statements reset the mode to a default value (&#145;non-local&#146; update mode), so, you have to set the local update mode at the beginning of each LUW in your program (if there is more than one).

What else?

There is at least one useful complement of local update mode usage. Suppose you have implemented a user exit which is called from within particular update function module &#150; for example, there are such user exits in sales documents. What to do if you need to transfer data from calling program to such a user exit? Sometimes it can be done via additional fields of application tables, but not always &#150; for example, you need to transfer not a value but even an internal table. Using local update mode you can do this via IMPORT/EXPORT statements &#150; as all the update function modules run in the same task and share the same task memory.

Certainly the local update mode is useful in particular circumstances &#150; it is not useful or even it is undesirable to use it in dialog transaction with one LUW as local update mode can increase system response time.

You can obtain more deatils concerning udate in SAP here: Updates in the SAP System, Update Techniques.

Filter Blog

By date: