ABAP log

December 18, 2007

Dusty ABAP snippets.

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

Many code samples or tips I post here originate in my collection that was built in last years. When I discover or code something worth keeping, I strip company specifics from the code and save it in simple text files. Some code snippets are used again and again, like macros in this post below or data caching forms. But once in a while I have to reuse the code after blowing away the dust.

That was back in 2000 when I have to do some report that, among other things, had to show costing estimates from the product costing runs. I don’t remember any more how did I find the solution (probably after my functional analyst gave up trying to help me to get the data easily), but it was using a couple of BAPI functions. And two weeks ago, after getting a similar requirement and having spent some time with debugger and asking FI/CO colleagues and everyone else, I have thought that this is actually a “deja vu”. Quick check of my code library – YES! I had to do some adjustments to the code and use the debugger to get the right constants corresponding to my company’s logic, but was still a wonder that my old code did. Here it goes:

* Macro to prefill select option

DEFINE ADD_SELECTION_VALUE.

  &2-SIGN = 'I'.

  &2-OPTION = 'EQ'.

  &2-LOW = &1.

  APPEND &2.

  CLEAR &2.

END-OF-DEFINITION.

form get_cost_element
  using
    pi_plant type werks_d
    pi_matnr type matnr
    pi_date  type sydatum
  changing
    po_cost  type total_amt.

  data:
    lt_cvar  type standard table of bapicstgva with header line,
    lt_matnr type standard table of bapimateri with header line,
    lt_plant type standard table of bapiplant  with header line,
    lt_costs type standard table of bapisplitt with header line.

  data: begin of it_list occurs 0.
          include structure bapicolist.
  data: end of it_list.

  data:
    ls_cost_header like bapiheader.

  data:
    ls_ret   type bapireturn,
    lf_found type char1 value ' '.

* ABCD is the costing variant. It should be your company specifics.
  add_selection_value 'ABDC' lt_cvar.
  add_selection_value pi_matnr lt_matnr.
  add_selection_value pi_plant lt_plant.

  clear: po_cost.

  call function 'BAPI_COSTESTIMATE_GETLIST'
       importing
            return             = ls_ret
       tables
            costing_variant    = lt_cvar
            material           = lt_matnr
            plant              = lt_plant
            cost_estimate_list = it_list
       exceptions
            others             = 1.
  check sy-subrc = 0.
* sort to get result of the last costing run
  sort it_list by valid_from descending.
* XX and YYY should be specific to your task.
* Check with debugger after calling the first BAPI
  loop at it_list where cstg_type = 'XX' and
                        vltn_vrnt = 'YYY'.
    if lf_found = 'X'.
      exit.
    endif.
*   Get first costing suitable by date
    if pi_date >= it_list-valid_from and
       pi_date  0.
          po_cost = lt_costs-total_amt.
          lf_found = 'X'.
          exit.
        endloop.
      endif.
    endif.
  endloop.

endform.

December 9, 2007

New frontiers of APO

Filed under: ABAP, APO, SAP — abaplog @ 3:26 pm

The capture of new markets by SAP flagship solutions like APO is sometimes missed by the press. But you can still discover the advances in the least expected ways.

This happened to me when the girl in our drug store offered me the best solution she had to my flu problem, earlier this year:

apo.jpg

Well, as long as the solution is “Made in Germany”, it doesn’t hurt, right? So I decided to give it a try and I still remain very satisfied with it. After all, the cost is quite reasonable and you DO get ROI after just several days of use! (No kidding here. I normally don’t trust the homoeopathic stuff but this one works wonderfully.)

So, let me give you my advice:

If your APO makes you sick, you just need a different version. Like that one on the photo above! 

November 22, 2007

Your fault! (Using SAP logging in your ABAP.)

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

The most common problem explanation we hear from users is that there should be a program error that triggers when users do everything right. While we never get rid of mistakes we do, we have to admit this special class of elusive errors that magically disappear as soon as we can trace every critical step in the program flow. The key to solution is logging. Sure, you cannot log everything, but only the most critical points. What I have to do sometimes is to add a temporary logging capability to investigate problem popping up in production system. So, let’s see what we can do to give us more chances to smack users with a log and say “Your fault!” :-)

There are numerous simple ways to show and store messages from your program. What’s important is that they have to be stored for some time to allow analysis after they happen. For example, if it’s a report or a program that is run in background most of the time, you have obvious choices of printing messages into the spool with normal WRITEs or using MESSAGE to have it stored in the job log. Both things can be found then in SM37.

If you want logging capabilities for an online report or a dialog program, you have to use other techniques. It is the same also for user exits that are called from SAP standard transactions or things that run in background by definition, like interfaces (e.g. APO CIF).

The simplest trick I am using in development to check program logic that cannot be easily debugged is my own table, a local object, with a long text field to store my messages and probably couple of other fields like date/time to distinguish the records. The table content is written by simple MODIFY. I usually have a program that zaps the content of my logging table, to clean up the mess.

When you are going to use this kind of logging in a “real” system, some things have to be taken care of:

  1. Uniqueness of records / keys, especially in case when the messages can be written by different program instances running at the same time
  2. More intelligent way of log reorganization (schedule).

When in development system, I can simply use the current time as the table key because I know that “I’m alone”. The official way of generating unique keys in SAP, however, is using number ranges. Briefly, you have number ranges for, say, production orders, and number ranges for materials etc. For each number range object, SAP can generate you the “next number” when you call a special function. This functionality is managed centrally and numbers will be unique even if you call the function on different application servers. The disadvantage of number ranges is that they have to be customized and monitored for “exhaustion”.

To save us the effort of creating a new number range object, customizing its range and monitoring, we can use another technique of generating unique numbers (or strings) that can then be used as part of table key: Global Unique Identifiers (GUIDs). Normally this is a long string, generated to be “almost” unique for a given system. The chance of having duplicate GUIDs is so small that I have never seen any checks whether to ID exists already. SAP uses GUIDs everywhere in APO, providing tables that map readable names / codes and internal identifiers – GUIDs. Those are normally 22 or 32 characters long. You can have a GUID generated for you by calling the function GUID_CREATE. When you add date/time as key fields into your logging table, you get a pretty simple logging system without any customizing. An additional reorganization program can be run periodically to have the entries older than, say, one month, deleted. Alternatively, if some errors have to be analysed and fixed (like problems with missing master data etc), you can provide table maintenance dialog (SE11/SE54) so that responsible user can delete checked log records after analysing them.

And finally let’s have a look at the real logging, as blessed by SAP. SAP Application Log, transaction SLG1, is the most common way to store messages. Log entries are created and stored using a set of functions. Logs are grouped by log objects and subobjects. For example, you have log group for object PPORDER (production order) and you can further classify logs with production order subobjects HEADER and OPERATION. Those are selections that you use on the initial screen of SLG1, along with others like: free text name / ID, date/time, user name and transaction code.

When creating a log entry, you can tell the system how long should it be stored before it’s deleted automatically. Unlike in the above case with your own log table, you don’t need a reorganization program. I normally request the log messages to be stored for 30 days, it can be less if you feel like your system will not be happy to carry the weight.

Enough with discussion, let’s see the code now:

* this include contains log system constants and has to be used
* in your program (or top include if it's a function group)
  include sbal_constants.

* the form writes a single log entry to application log,
* building log entry identifier from parameters pf_par1 and pf_par2,
* and writing the content of other parameters into the message
  form write_log
    using pf_par1
          pf_par2
          pf_str1  type  char50
          pf_str2  type  char50
          pf_str3  type  char50
          pf_str4  type  char50.

    data:
       ls_log        type bal_s_log,
       lt_handle     type bal_t_logh,
       lf_handle     type balloghndl,
       ls_msg        type bal_s_msg.

* we use production order / operation log objects
    ls_log-object     = 'PPORDER'.
    ls_log-subobject  = 'OPERATION'.
    concatenate 'Some_Name_' pf_par1 '_' pf_par2
      into ls_log-extnumber.
    ls_log-aluser     = sy-uname.
    ls_log-alprog     = sy-repid.
    ls_log-altcode    = 'YOUR_TCODE'.
    ls_log-aldate_del = sy-datum + 30.  "keep for one month
    ls_log-del_before = 'X'.

*   create a log
    call function 'BAL_LOG_CREATE'
         exporting
              i_s_log      = ls_log
         importing
              e_log_handle = lf_handle
         exceptions
              others       = 1.

*   define data of message for Application Log
*   Use generic message template with & & & &
    ls_msg-msgty     = 'S'.
    ls_msg-msgid     = '01'.
    ls_msg-msgno     = '319'.
    ls_msg-msgv1     = pf_str1.
    ls_msg-msgv2     = pf_str2.
    ls_msg-msgv3     = pf_str3.
    ls_msg-msgv4     = pf_str4.
    ls_msg-probclass = probclass_none.

*   add this message to log
*   this function can be called several times to have one log entry
*   store several different messages
    call function 'BAL_LOG_MSG_ADD'
         exporting
              i_log_handle = lf_handle
              i_s_msg      = ls_msg
         exceptions
              others       = 1.

*   save the log
    append lf_handle to lt_handle.
    call function 'BAL_DB_SAVE'
         exporting
              i_save_all     = 'X'
              i_t_log_handle = lt_handle
         exceptions
              others         = 1.

  endform.

Note that here we are using a generic message (01/319). This way you are free to write anything you want but then you don’t get “long text” of the message when clicking it in the SLG1. Alternatively, we could use application specific message class, assigning it and message variables to appropriate fields of the structure bal_s_msg.

November 12, 2007

Copying SAP ALV layouts between clients.

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

When my colleague wanted to check some standard SAP reports, he got the following problem: the system had two different clients (mandants), one of them had the test data and in another one the ALV layouts were saved. You can create a transport request from ALV maintenance screen when in the list output, but for that, you had to have some data (this particular report stopped with an error message at the selection screen). It was known to him that ALV layouts can be transported with a separate program, RKKBALVI, but it seems that the program name indicated in the status line was a different one. After doing the usual trick with debugger (going to layout transport screen and entering /h to get into debugger), we could figure out that the report RKKBALVI actually calls the function module LT_VARIANTS_IMPORT_SELECTION, which can also be used separately. But best of all, we could notice in the debugger that the program name used for RKKBALVI was completely wrong! With the right program name, both ways (function module and SAP-supplied program) worked perfectly.

October 10, 2007

Navigating component screen of CO02 with BDC.

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

Creating and changing production orders in SAP R/3 is a common task in production planing. While you can easily create an order from your ABAP program and do some simple changes using BDC technique, things get ugly when you need to do some more complex tasks. Unfortunately, there are no BAPIs or official function modules to cover every possible operation. Creating orders and changing their operation parameters can be done using “Production Optimization Interface” functions, as I described once here on ABAPlog. Yes, we all know that a couple of years ago SAP promised us to cover complete functionality of SAP with its SOA stuff (ESA in SAP lingo) till the end of 2007, but it doesn’t look like they are going to hit that. Therefore, we poor ABAP developers have to stick with BDC as the last but proven solution.

The most common requirement, apart from changing the header and operation data, is changing the component list. This could be relatively easy with BDC, but only if you are always sure that you have just a few of components that fit on the same page of the table control in the component overview of CO02. Unlucky ones have to deal with the navigation. Some general ideas are given in the BDC article of the ABAP Knowledge Corner from Richard Harper. The main point is: look for navigation commands in menu. Even if you don’t see them, you still have a chance that they are not present in the GUI status but nevertheless supported (you have to study the source code of the SAP transaction to find that out). But CO02 have a wonderful command in its component overview screen: “Find”. Clicking it brings a popup window:

co02.gif

For BCD, it’s the screen 0110 of the program SAPLCO05. By filling some parameters and clicking Enter, you land on the corresponding line. This line will be the first one in the table control and thus easily handles by your batch input. Before using the navigation screen, it is normally a good idea to navigate to the top of the list using the corresponding command.

Let’s see how can we use the navigation. I assume first that each ABAP BDC program defines a form to populate BDC data table, something like this:

form dynpro using dynbegin name value.
  if dynbegin = 'X'.
    clear t_bdc_tab.
    move: name                    to t_bdc_tab-program,
          value                   to t_bdc_tab-dynpro,
          'X'                     to t_bdc_tab-dynbegin.
    append t_bdc_tab.
  else.
    clear t_bdc_tab.
    move: name                    to t_bdc_tab-fnam,
          value                   to t_bdc_tab-fval.
    append t_bdc_tab.
  endif.
endform.

The following form will change the sort string (normally the very last column) for item identified with position number and material:

form change_sort_string
  using p_pos p_newsortval p_matnr.
  perform dynpro using:
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_OKCODE' 'P--',  "Go to top pos first
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_OKCODE' 'AUFS',  "Find item
    'X' 'SAPLCO05' '0110',
    ' ' 'RCOSU-POSNR' p_pos,
    ' ' 'RCOSU-MATNR' p_matnr,
    ' ' 'BDC_OKCODE' 'MORE',  "Enter to navigate
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_CURSOR' 'RESBD-SORTF(01)',
    ' ' 'RESBD-SORTF(01)' p_newsortval,
    ' ' 'BDC_OKCODE' '/00',  "Enter to change pos number
    'X' 'SAPLCOMD' '0110',
    ' ' 'BDC_OKCODE' '/00'.  "confirm
endform.

And this one will delete the item, also identified with its position number and material:

form delete_pos_by_number using    p_posnr p_matnr.
  perform dynpro using:
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_OKCODE' 'P--',  "Go to top pos first
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_OKCODE' 'AUFS',  "Find item
    'X' 'SAPLCO05' '0110',
    ' ' 'RCOSU-POSNR' p_posnr,
    ' ' 'RCOSU-MATNR' p_matnr,
    ' ' 'BDC_OKCODE' 'MORE',  "Enter to navigate
    'X' 'SAPLCOMK' '0120',
    ' ' 'BDC_CURSOR' 'RESBD-POSNR(01)',
    ' ' 'RC27X-FLG_SEL(01)' 'X',  "select 1st item in the list
    ' ' 'BDC_OKCODE' 'DEL',  "delete selected item
    'X' 'SAPLCOMD' '0110',
    ' ' 'BDC_OKCODE' '/00'.  "confirm
endform.

October 4, 2007

Using ABAP Native SQL with SAP Namespaces.

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

Starting, I think, with SAP R/3 4.6, we can use namespaces to name our objects. (“We” includes, in this case, SAP itself too.) That means that instead of using the Y- or Z-prefix to distinguish between SAP’s objects (like tables or programs) and customer or third-party objects, we can register a namespace with SAP that will be used as object name prefix exclusively by our company, thus preventing name conflicts. SAP itself is using, for example, /SAPAPO/ namespace for all objects specific to APO. The feature was logical to expect from SAP to add it, but sometimes it is bringing problems.

Till now all I had were some warning messages when activating tables or ABAP programs (already forgot what was that exactly). But today, when I was writing DELETEs in Native SQL, I had to spend some time to figure out what kind of syntax does it want. The problem was that when the native SQL statement was sent to our IBM DB6 database, it was causing a short dump. I did not expect it to be happy though, and tried to enclose my table names that had slashes in them into single quotes. Doesn’t work. I used the SQL Trace (ST05) then to see which syntax does SAP use internally. They used double quotes. (Note that unless it is the native SQL, double quotes will be considered a comment opening.) But double quotes didn’t help me either – it was still crashing.

I did a quick search in OSS and found one rather unrelated note that among other things mentioned that in addition to double quotes, all table and column names should be in upper case. This was my last trouble to fix. My ABAP Pretty Printer setting is “all lowercase”, and though the pretty printer doesn’t touch the native SQL, I typed everything in lower case myself. If I only copied the syntax that was shown in the “Detail” screen of the SQL Trace, my problem would be solved right away! But well, it was a nice little trick for me to learn.

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.

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 31, 2007

SAP SDN? Thanks, just looking…

Filed under: ABAP, SAP, SDN — abaplog @ 8:07 pm

I did never participate in user forums at SAP SDN. Maybe it’s just this feeling of bad treatment of us ABAPers by SAP that lives inside of me since late nineties. (If you don’t get what I mean: SAP doesn’t provide any viable technical documentation about the system, except for ABAP language itself and some other less important pieces). They overtook (or just attracted, with no migration?) so many participants that we old farts living at places like Sapfans could probably be jealous: the number of topic and replies they get per a single day is what we get probably in a week. There has been a lot of critique that the point and reward system is attracting all kind lazy and greedy, leaving knowledgeable folks in the shade. And now I read this topic, being pointed by a Sapfans fellow.

Where this whole thing is heading to? This doesn’t even deserve an RTFM kind of response. The topic of spoonfeeding is completely dead at other places but SDN seems to be welcoming all this. Forget about the fact that about a half of responses are totally wrong! Looks like nothing can be more encouraging to volunteer for help than that pure good feeling after, anything else just ruins the best ideas.

You mean, help me? Mmmmm… No thanks, just looking!

« Newer PostsOlder Posts »

Blog at WordPress.com.