ABAP log

September 24, 2007

Comparing two SAP Query infosets without SQ02.

Filed under: ABAP, SAP — abaplog @ 8:33 pm

One of the nice features of SAP Query is that when you build an infoset for it, you can, in addition to reading data directly from SAP database tables, use plain ABAP code to implement some custom data extraction logic. The only problem with that is security. Normally, SAP Query is used by both developers and end-users, so that many companies allow building queries directly in test or even production systems. But who wants ABAP being written directly in production? On our system you can still build queries based on existing infosets, but changing the infosets is allowed only in our development system. This works well, the only problem is that in the very beginning, changing the infosets was possible in the production system too. And now, before changing an infoset in development, we have to compare its current version against one in the production system. Without having an authorization for SQ02, of course.

As I often do, I looked for alternatives. SQ02 allows you to print a kind of report where the whole infoset, along with selection logic and even manually written ABAP will be shown. And as it often happens, this is done by some ABAP report being called by the transaction, with no additional authority checks in report. Which means, I can get the whole structure of my infoset without SQ02, just by running this report. The report’s name is RSAQSHSG, and all it needs is the infoset name and the output mode, where you can put ‘A’ to see the whole. No tricks, simple and legal.

Advertisements

September 17, 2007

Can’t run SAP database utility? Nevemind.

Filed under: ABAP, SAP, security — abaplog @ 8:03 pm

When I change a database table, adding or deleting fields for example, I always have to use the database utility to have DDL commands like ALTER TABLE executed on the underlying SAP database. This works well in a development environment, but normally it is (and has to be) prohibited for a normal developer in other systems. Things can get even more complex if I am doing tests of, say, a new index in some “other” system and want to drop and recreate an index during my tests. Looks for a help from guys guarding the system? Forget the tests then. But I always remember that many “forbidden” tasks can be done by running some standard report. There is always a great chance that all you need is an authorization for SE38 and the report you use doesn’t do any further checks.

This was exactly the case with the database utility. The screen that comes in SE14 is actually nothing else than a wrapper program that asks you for parameters (what has to be done) and then runs a report to do the job. If you request this to be done in background then of course you can see which program was run and what were the parameters passed. The possible parameter values of this program can be simply learnt by looking at its ABAP code. That was what I’ve done and this saved me some hassles during testing.

The fun is that the program doesn’t do any checks at all – it’s supposed to be run exclusively by SE14. No popup questions to confirm anything. (Hey, are you sure you want to DROP this TADIR?) No matter which system. Can be quite useful if your just-transported database index brings the production system down – you can drop it quickly. If you are lucky to don’t mistype the parameter values.

September 6, 2007

Caught by TABIX.

Filed under: ABAP, SAP — abaplog @ 8:23 pm

One of my little ABAP programs surprised me today by crashing into ST22. It was just a simple counter taking values from SY-TABIX in an internal table LOOP. I looked again and again. It seemed that a variable of type INT4 (declared to be compatible with SY-TABIX, as I thought) could not accumulate a five-digit number. The solution was simply to have a fresh look at the underlying data type into Data Dictionary. Here, look at my test program:

report ztest.

data:
  lf_tabix  type sytabix,
  lf_tabix2 type tabix.

lf_tabix = 10000.
write: / lf_tabix.
lf_tabix2 = 10000.
write: / lf_tabix2. 

After clicking several times on the type TABIX, I have finally noticed that TABIX is a structure! Four bytes, occupied by one field. And when you use the structure as a whole, ABAP treats it like a character string, and any number greater than 9999 gets screwed.

Well… time to retake BC400.

August 29, 2007

The best ABAP short dump.

Filed under: ABAP, SAP — abaplog @ 7:25 pm

We all love ST22. I have seen many funny messages there. And this one, well… it’s a tribute to SAP short dumps. See for yourself and have fun.

Note: The image can be resized by your browser and be hardly readable. Clicking the mouse over it helps to get the original size.

August 15, 2007

Reading a comma delimited file in ABAP (aka CSV file).

Filed under: ABAP, SAP — abaplog @ 8:51 pm

Download functions of SAP allow you to write data into a file on the presentation server in many different formats. However, reading files (uploading) is very limited. A common question asked on forums is how to upload a comma delimited file. This format, also known as CSV, is used by many programs including MS Excel. For example, you may want to save an Excel spreadsheet into CSV file and then upload it into SAP. Unfortunately, there is no function that can help you.

Few years ago I had to extend an ABAP program with a capability to read CSV files. I wrote a very simple parser that can read files with any delimiter, not only comma. (It actually has to be a printable character, control characters will most likely not work). The delimiter can be set as a constant in the body of the parser form. It can be easily changed to be a form parameter. The code of the parser can be downloaded here. NOTE: use “view source” function of your browser – there are field symbols that are interpreted as HTML tags (I’ll have to fix that…)

If you find any problems that have to be fixed, please send your suggestions (the best would be some working code), as a comment on this article.

July 23, 2007

Displaying SAP error messages in a nice way.

Filed under: ABAP, SAP — abaplog @ 8:09 pm

One day I wanted to have my error messages displayed in a nice popup window (users just wanted it to be “RED” and shouting), but not with using the MESSAGE statement that either stops the processing or is shown just as “information” (type “I”). I tried this and that, and almost gave up to using a simple “information” message as I discovered that my colleague had a similar task. He had a luck to find the function module I needed: C14ALD_BAPIRET2_SHOW. His problem was that it takes a table of BAPIRET2 as an argument (with message class and number) but his messages were coming from an external system. This way, the message class did not exis, which SAP didn’t like, showing some strange output. However, he had a complete error message (in BAPIRET2-MESSAGE). We decided to split the message into four (or up to four) parts and pretend that we have actually the message 001 from the message class 00 (whose template looks like &1&2&3&4 and exists on all SAP systems). This way a user could see the complete message, not being screwed up by SAP like it does if it sees some unknown message class. And my task was even much simpler because everything was happening in one system (no problems with wrong message classes).

May 19, 2007

Creating and changing a production order from ABAP.

Filed under: ABAP, SAP — abaplog @ 9:21 pm

Creating production orders is one of common tasks when you have some external system that does the planning. Even if you work “directly” in R/3, you will probably need to create or change production orders. The SAP’s standard answer – BDC – is not really performant if you are processing a lot of data. To disappointment of any ABAPer, SAP provides no BAPIs to deal with that task (they do for order confirmations though). But fortunately, there are some other function modules that can help us. Before going further, I have to say that those functions can help you to create a production order, change its header parameters like scheduled start, or change its operations. Changing components, documents or production resources still has to be done with BDC (CO01 or CO02).

For production orders, SAP has implemented an interface for external planning systems (and even provided some documentation!). Their own “external” system, APO, does not use those functions (actually, one function supporting different tasks), but they are kind of official and you can find some documents on them in SAP help. Of course, the use is not limited to external systems and the function can be used directly from ABAP programs.

So, let’s start with creating an order. The function that we are going to use is CLOI_CHANGES_UPL_31. Don’t get confused by the “31” in its name. It works perfectly in 4.6 and (most likely) in later version of R/3. There is one inportant point that you have to keep in mind when working with the function: you are given just one call per session. That means, you can create one order or many orders, but with one call. After that, some internal state data is messed up and to save us from initializing it (which has to be done with some really internal functions), we better start a new sesion if we need to call the function again. So, in my example code below I am going to check whether the current session is not the last one allowed for the current user and call the function in a new task. Note that we have to do the check before EACH call unless we are really sure (I do it only once in the sample code below). Since it’s going to be an anynchronous call, we need to suspend our program until the function running in parallel finishes and get the return data into our program. This is achieved with standard SAP constructs like “performing … on end of task” in the CALL FUNCTION statement and “receive results” in the form that is performed. I am also using a global variable that tells me that the call is over. You can find more useful information in SAP OSS note 545860 that described those specifics about calling that function.

* 1. Create a production order.

data: t_cloi_ord_exp          type standard table of cloiord,
      lw_cloi_ord_exp         type cloiord,
      lt_cloi_msg_obj_log_exp type standard table of cloimoblog,
      lt_cloi_methods_exp     type standard table of cloimetlog,
      t_cloi_messages_exp     type standard table of cloimsglog,
      lw_cloi_messages_exp    type cloimsglog,
      ls_cloi_if_par_v2       like cloiifpar.

data:     lt_cloi_ordu        type standard table of cloiordu,
          lw_cloi_ordu        type cloiordu,
          lt_cloi_ordi        type standard table of cloiordi,
          lw_cloi_ordi        type cloiordi,
          lt_cloi_ord_opru    type standard table of CLOIOPERU,
          lw_cloi_ord_opru    type CLOIOPERU.

data:
  lf_date   like sy-datum,
  lf_date2  like sy-datum,
  f_flag(1) type c,
  f_aufnr   like aufk-aufnr.

data: f_act_sess like sm04dic-counter,
      f_max_sess like sm04dic-counter.


lf_date = sy-datum + 7.   "let's try to schedule it next week
lf_date2 = sy-datum + 14.

clear: ls_cloi_if_par_v2.

* those fields below are described in SAP help, but nevermind...
ls_cloi_if_par_v2-commitflg   = 'C'.
ls_cloi_if_par_v2-r3_version  = '40'.
ls_cloi_if_par_v2-metlog_req  = 'X'.
ls_cloi_if_par_v2-msglog_req  = 'X'.
ls_cloi_if_par_v2-msgobj_req  = 'X'.
ls_cloi_if_par_v2-ord_req     = 'X'.
ls_cloi_if_par_v2-ordseq_req  = 'X'.
ls_cloi_if_par_v2-ordopr_req  = 'X'.

* in case if we have external number range,
* we have to provide the future order number
lw_cloi_ordi-extaufnr = '1234567890'.

* material
lw_cloi_ordi-field =  'MATNR'.
lw_cloi_ordi-value =  'MATNUM1'.
append lw_cloi_ordi to lt_cloi_ordi.

* plant
lw_cloi_ordi-field =  'WERKS'.
lw_cloi_ordi-value = 'W001'.
append lw_cloi_ordi to lt_cloi_ordi.

* order type
lw_cloi_ordi-field =  'AUART'.
lw_cloi_ordi-value = 'PP01'.
append lw_cloi_ordi to lt_cloi_ordi.

* quantity
lw_cloi_ordi-field =  'BDMNG'.
lw_cloi_ordi-value = '100'.
append lw_cloi_ordi to lt_cloi_ordi.

lw_cloi_ordi-field =  'GLTRP'.    "finish date
lw_cloi_ordi-value = lf_date2.
append lw_cloi_ordi to lt_cloi_ordi.

lw_cloi_ordi-field =  'GSTRP'.    "start date
lw_cloi_ordi-value = lf_date.
append lw_cloi_ordi to lt_cloi_ordi.

* now check if we have one free session
call function 'TH_USER_INFO'
     importing
          act_sessions = f_act_sess
          max_sessions = f_max_sess
     exceptions
          others       = 1.

if sy-subrc  0 or f_act_sess >= f_max_sess.
  message e208(00) with 'No more free sessions'.
endif.

* and finally, let it happen!
clear: f_flag.
call function 'CLOI_CHANGES_UPL_31'
     starting new task 'CLOI_TEST'
     performing receive_res on end of task
     exporting
          cloi_if_par          = ls_cloi_if_par_v2
     tables
          cloi_ordi_imp        = lt_cloi_ordi
          cloi_method_log_exp  = lt_cloi_methods_exp
          cloi_message_log_exp = t_cloi_messages_exp
          cloi_msg_obj_log_exp = lt_cloi_msg_obj_log_exp
          cloi_ord_exp         = t_cloi_ord_exp.

* wait till the global variable will be set by our form
wait until f_flag = 'X'.

This is the form that is used to receive results and signalize
the end of processing:

form receive_res using taskname.

  data: lt_cloi_msg_obj_log_exp type standard table of cloimoblog,
        lt_cloi_methods_exp     type standard table of cloimetlog.

  receive results from function 'CLOI_CHANGES_UPL_31'
     tables
       cloi_method_log_exp        = lt_cloi_methods_exp
       cloi_message_log_exp       = t_cloi_messages_exp
       cloi_msg_obj_log_exp       = lt_cloi_msg_obj_log_exp
       cloi_ord_exp               = t_cloi_ord_exp.

  f_flag = 'X'.

endform.

The list of created production orders is returned back in the global table t_cloi_ord_exp. Now, let’s release them. (Setting, for example, technically complete status is similar). We are going to use the parameter cloi_ordu_imp instead of cloi_ordi_imp (I for insert, U for update). We populate the field FIELD with the value “METHOD” to say that we are not setting any values but we want to call a “method” of releasing the order (method in the same sense as in object programming).

* release created prod order

clear: lt_cloi_ordu.
loop at t_cloi_ord_exp into lw_cloi_ord_exp.
  check not lw_cloi_ord_exp-aufnr is initial.
  clear: lw_cloi_ordu.
  lw_cloi_ordu-aufnr  =  lw_cloi_ord_exp-aufnr.
  f_aufnr  =  lw_cloi_ord_exp-aufnr.
*   Methods:
*     SetTechnicalComplete
*     RevokeTechnicalCompletion
*     Schedule
*     Release
*     SetUserStatus
*     RevokeUserStatus
  lw_cloi_ordu-field  =  'METHOD'.
  lw_cloi_ordu-value  =  'Release'.
  append lw_cloi_ordu to lt_cloi_ordu.
endloop.

clear: lt_cloi_methods_exp, t_cloi_messages_exp,
       lt_cloi_msg_obj_log_exp, t_cloi_ord_exp.

clear: f_flag.
call function 'CLOI_CHANGES_UPL_31'
     starting new task 'CLOI_TEST'
     performing receive_res on end of task
     exporting
          cloi_if_par          = ls_cloi_if_par_v2
     tables
          cloi_ordu_imp        = lt_cloi_ordu
          cloi_method_log_exp  = lt_cloi_methods_exp
          cloi_message_log_exp = t_cloi_messages_exp
          cloi_msg_obj_log_exp = lt_cloi_msg_obj_log_exp
          cloi_ord_exp         = t_cloi_ord_exp.

wait until f_flag = 'X'.

Now, as the order is created and released, let’s do some simple change. We are going to change the workcenter of the order’s operation.

* change the work center of the operation 0010

clear: lt_cloi_ord_opru.
clear: lw_cloi_ord_opru.
lw_cloi_ord_opru-aufnr  =  f_aufnr.
lw_cloi_ord_opru-aplfl  =  0.
lw_cloi_ord_opru-vornr  =  '0010'.
lw_cloi_ord_opru-field  =  'ARBID'.
* note that this is the internal ID of workcenter!
lw_cloi_ord_opru-value  =  '10000001'.
append lw_cloi_ord_opru to lt_cloi_ord_opru.

clear: lt_cloi_methods_exp, t_cloi_messages_exp,
       lt_cloi_msg_obj_log_exp, t_cloi_ord_exp.

clear: f_flag.
call function 'CLOI_CHANGES_UPL_31'
     starting new task 'CLOI_TEST'
     performing receive_res on end of task
     exporting
          cloi_if_par          = ls_cloi_if_par_v2
     tables
          cloi_ord_opru_imp    = lt_cloi_ord_opru
          cloi_method_log_exp  = lt_cloi_methods_exp
          cloi_message_log_exp = t_cloi_messages_exp
          cloi_msg_obj_log_exp = lt_cloi_msg_obj_log_exp
          cloi_ord_exp         = t_cloi_ord_exp.

wait until f_flag = 'X'.

That’s it! The function that we use doesn’t let us do anything we may need, but it can be very useful anyways. It’s stable and “blessed” by SAP, and I have seen it used in third-party products that need to be integrated with SAP. If you have any doubts, or your supervisor does, don’t forget to point to the SAP help documentation. It’s a small reassurance that it’s not one of those “internal only” functions that are better to avoid. And, the main point will of course be the performance if compared with BDC.

May 2, 2007

SAP Locks Mystery.

Filed under: ABAP, SAP — abaplog @ 8:19 pm

I was kicked once by some problem that lies on how SAP behaves when you create locks with Enqueue functions. The locks were at the first glance mysteriously released (or seemed to allow concurrent locking, though it was requested as exclusive).

Using locks in SAP is easy, and the functionality is well described in SAP Help and in the ABAP Knowledge Corner article from Richard Harper.

So, because of the apparent simplicity, all that seemed very suspicious. After playing with debugger and SM12, I have found that the problem is with the parameter _SCOPE of the locking functions. By default, as generated by SAP template generator in SE38, it’s set to 2, which means it’s released in the calling program and transferred to the update task, if the program does such calls. As soon as the update is over, the lock is deleted. If you have COMMIT AND WAIT after the call function, it can be very amusing to have the lock gone. The solution was to set it to 1, so that the lock ownership is not transferred (the functions I call do not depend on the objects I lock).

To extend the standard documentation and the article from Rich, I would add the following regarding the parameters for ENQUEUE functions:

The parameter _SCOPE defines where the lock affects the data in case the locking program does update task calls (which may be implicitly done by calling normal function modules, BAPIs or transactions).

With ‘1’, the lock is released only when the ***caller*** program finishes or releases the lock explicitly. If the lock is done in exclusive mode, the update task cannot lock the same object (and normally cannot update it).

With ‘2’, the lock is “moved” to the update task. The update task may release it explicitly, otherwise the lock will be released when update task is finished. After calling the update task, the caller has nothing to do with the lock. Example pseudocode:

Example 1:

call ENQUEUE with _SCOPE=2
call function FUNC. “synchronous call – no update task
call DEQUEUE “clean up – lock is released

Example 2:

call ENQUEUE with _SCOPE=2
call function FUNC in update task. “async call
commit work and wait. “we want to wait for update till it’s done
call DEQUEUE “oops! we have nothing to release here! the lock is gone as soon as FUNC exits!
“Other processes can lock the same object, even as we may expect them to be denied

With ‘3’, the lock created by caller is “copied”. After that, both caller and update task own their locks. The locks will be released either explicitly with dequeue, or when respective process is done. If the original lock was exclusive, as long as one of those two locks exist, no other processes can obtain a new exclusive lock.

April 27, 2007

Copying ABAP program texts between two SAP systems / programs / languages.

Filed under: ABAP, SAP — abaplog @ 9:24 am

From time to time I need to transfer text elements between different SAP systems or ABAP programs without the hassle of the transport system. This happens if we have to do changes in “parallel” systems, or if I have a copy of a program but texts were not copied initially. Yet another case is quick copying of texts from one language to another without clicking a hundred times in the translation screen.

In this case I use two simple ABAP programs. One saves the text in specified language into a file on your PC. And the second one reads the texts back and writes them into the text pool of the second program.

************************
*     Download
************************
report zdowntex.

parameters:
  p_report like sy-repid,
  p_file like rlgrap-filename,
  p_lang like sy-langu.

data: it_text like textpool occurs 100 with header line.

start-of-selection.

  read textpool p_report into it_text language p_lang.

  if sy-subrc = 0.
    call function 'WS_DOWNLOAD'
        exporting
             filename            = p_file
             filetype            = 'DAT'
         tables
              data_tab            = it_text
         exceptions
              file_open_error     = 1
              file_write_error    = 2
              invalid_filesize    = 3
              invalid_table_width = 4
              invalid_type        = 5
              no_batch            = 6
              unknown_error       = 7
              others              = 8.
    write: / 'ws_download : ', sy-subrc.
  else.
    write: / 'read textpool : ', sy-subrc.
  endif.

************************
*     Upload
************************
report zuplotex.

parameters:
  p_report like sy-repid,
  p_file like rlgrap-filename,
  p_lang like sy-langu.

data: it_text like textpool occurs 100 with header line.

start-of-selection.

  call function 'WS_UPLOAD'
       exporting
            filename            = p_file
            filetype            = 'DAT'
       tables
            data_tab            = it_text
       exceptions
            conversion_error    = 1
            file_open_error     = 2
            file_read_error     = 3
            invalid_table_width = 4
            invalid_type        = 5
            no_batch            = 6
            unknown_error       = 7
            others              = 8.

  if sy-subrc = 0.
    insert textpool p_report from it_text language p_lang.
    write: / 'insert textpool : ', sy-subrc.
  else.
    write: / 'ws_upload : ', sy-subrc.
  endif.

April 25, 2007

Working with production order operation long texts from ABAP.

Filed under: ABAP, SAP — abaplog @ 6:03 am

The common task of changing the long text of a production order’s operation can bring some surprises that were discussed several times at Sapfans ABAP forum. Composing right text ID and key to feed the READ_TEXT and SAVE_TEXT functions is the first step. If you are unsure on that, some tips are provided in Sapfans ABAP FAQ, or you can use the debugger as an alternative. All that is relatively straightforward.

The first problem that is usually encountered is that after updating the long text with the function SAVE_TEXT, the new text is not visible in standard SAP transactions like CO03. The new text can be read with the function READ_TEXT though. The trick is that SAP uses the “long text exists” indicator, the field TXTSP in the table AFVC. To make the text “visible” to standard transactions, we have to set the TXTSP value to the current language (or the text’s language). Unfortunately, this has to be done with the direct UPDATE on the AFVC – there are no known workarounds.

Another problem can be seen when the same order is processed by users that use different languages. While SAP allows to store the long text for the same operation in several languages, actually, only one text object will be active at the same time in regard to standard transactions – the object in the last saved language. That means, when SAP writes the text back, the TXTSP will be overwritten with the current language (say, language A). If the next user is working in another language (language B), SAP will present the text in the “first” language (A) in the CO03 (even if the text in language B exists!). But after saving, the text will be written in the new language and TXTSP will be set accordingly.

So, when working with long texts directly, you have to read with READ_TEXT using the language stored in TXTSP, NOT with the current language. When saving, you save in the current language and set the TXTSP to the current language. This way you are consistent with what SAP does and this will prevent you from surprises in a multilingual environment.

« Newer PostsOlder Posts »

Create a free website or blog at WordPress.com.